Compare commits

...

53 Commits

Author SHA1 Message Date
Apprentice Harper
a7974f0f14 Update ineptpdf.py
integer division, and version
2021-01-03 16:11:02 +00:00
Apprentice Harper
ed412bee35 Updated to inept.pdf for PC
Contributed changes for PC compatibility. Thanks, Aldo.

Update main version to 7.0.2
2021-01-03 16:01:14 +00:00
Apprentice Harper
6cee615f26 Update ineptpdf.py
Fix handling of metadata
2021-01-03 15:35:17 +00:00
Apprentice Harper
c4581b4d72 Version to 7.0.1, ineptpdf fixes
ineptpdf should now decrypt at least some Adobe PDFs
2020-12-30 12:14:04 +00:00
Apprentice Harper
f6a568bcc1 Update ineptepub.py
Handle uncompressed elements (if any) in the zip file.
2020-12-27 12:16:11 +00:00
Apprentice Harper
bf6170e613 Merge pull request #1445 from ableeker/python3
Some more fixes for ePub
2020-12-26 16:02:12 +00:00
Apprentice Harper
afcd79c0cc Merge pull request #1443 from jony0008/master
Update sv translation
2020-12-26 16:00:04 +00:00
Apprentice Harper
fdf0389936 MobiDeDRM fixes
Change handling of PIDs to cope with byte arrays or strings passed in. Also fixed handling of a very old default key format.
2020-12-26 15:58:42 +00:00
Aldo Bleeker
5599c1694b Some more fixes for ePub 2020-12-26 15:36:10 +01:00
Jony
dff90fae6f Update sv translation 2020-12-25 12:47:14 +00:00
Apprentice Harper
d33f679eae Merge pull request #1413 from ableeker/python3
Small fix to make Obok help link work.
2020-12-13 11:28:51 +00:00
Aldo Bleeker
225e74a334 Small fix to make Obok help work. 2020-12-09 17:34:24 +01:00
Apprentice Harper
13e9a14907 Merge pull request #1398 from xxyzz/config
return str from load_resource()
2020-12-04 12:52:42 +00:00
Apprentice Harper
92ea0a2f24 Merge pull request #1392 from penenkel/patch-1
Add conversion from bytearray to bytes so that pids are hashable
2020-12-04 12:51:26 +00:00
xxyzz
a1059650f6 return str from load_resource() 2020-12-03 19:02:09 +08:00
penenkel
a3cc221932 Revert changes to k4mobidedrm.py 2020-12-02 22:36:29 +01:00
penenkel
6732be1434 getPidList() now returns pids as bytes instead of bytearrays 2020-12-02 22:34:29 +01:00
penenkel
ad5cb056f0 Add conversion from bytearray to bytes so that pids are hashable 2020-11-30 23:25:01 +01:00
Apprentice Harper
d3c7388327 Merge pull request #1389 from ableeker/python3
Python 3 fixes for __init__.py
2020-11-29 16:35:46 +00:00
Aldo Bleeker
8e436ad920 Python 3 fixes fort correct version of __init__.py 2020-11-29 16:54:45 +01:00
Aldo Bleeker
ae806f734e Python 3 fixes for __init__.py 2020-11-29 13:39:04 +01:00
Apprentice Harper
ccfa454226 Merge branch 'Python2' - the DeDRM plugn version change 2020-11-29 10:47:09 +00:00
Apprentice Harper
6716db1f62 Derive calibre version tuple from __version__ string 2020-11-29 10:40:14 +00:00
Apprentice Harper
0e0d7d8b14 Don't rule out running from the command line 2020-11-28 16:25:54 +00:00
Apprentice Harper
981aadc497 Merge pull request #1380 from xxyzz/byte-string
Fix byte string error for KFX
2020-11-28 16:19:17 +00:00
Apprentice Harper
26eb5d676c Merge branch 'Python2' Bring across version number updates from 6.8.1 release 2020-11-28 16:18:09 +00:00
Apprentice Harper
464788a3f1 Update DeDRM version number to 6.8.1, and kindlekey to 2.8 2020-11-28 16:11:17 +00:00
Apprentice Harper
036f9007fd Merge branch 'Python2': Get the changes to fix Kindle key retrieval for Mac OS X Big Sur 2020-11-28 16:07:31 +00:00
Apprentice Harper
bdd1c2e474 Merge pull request #1383 from ableeker/python3
Python 3 fixes for Barnes&Noble
2020-11-28 15:47:22 +00:00
Apprentice Harper
54a58d05a5 Merge pull request #1382 from koumaza/koumaza/refine-github-actions-workflow
Refine GitHub Actions Workflow
2020-11-28 15:45:43 +00:00
Apprentice Harper
218539f131 Merge pull request #1381 from protochron/fix_big_sur_python_2
Fix loading libcrypto on OSX Big Sur
2020-11-28 15:44:42 +00:00
Aldo Bleeker
f9d9b6016f Python 3 fixes for Barnes&Noble 2020-11-28 14:49:27 +01:00
shanghai yakisoba chan!
131cea1215 Update Format.yaml: Change execution condition of workflow
Execute format workflow only if there is `!format` in the commit message.
2020-11-28 21:39:27 +09:00
shanghai yakisoba chan!
731eeac087 Refine gh-actions
* Update and rename Python_test.yml to Lint.yaml
* Create Format.yaml
2020-11-28 15:48:31 +09:00
Dan Norris
cdab22e59c Fix loading libcrypto on OSX Big Sur
It looks like Big Sur removed `libcrypto.dylib` as a file on the
filesystem, so loading it using `ctypes.find_library` fails which breaks
Kindle decryption. Now to load a dylib you need to attempt to load it
directly and the operating system will load the dylib from the OS' cache
or fail.

This fixes the problem by explicitly setting the path to libcrypto to
`/usr/lib/libcrypto.dylib` if `ctypes.find_library` does not find the
file, loading the dylib and raising an exception if it fails at that
point.

See saltstack/salt#5778 for more detailed info.

Closes #1369.
2020-11-27 22:28:34 -05:00
xxyzz
b8b324956c replace bord with ord and some other byte string fix
PyCryptodome's bord() in Python3 does nothing.
2020-11-28 11:22:27 +08:00
xxyzz
1955b34883 import ion correctly 2020-11-28 11:20:53 +08:00
Apprentice Harper
dbc5c2b4de Merge pull request #1269 from keshavgbpecdelhi/patch-4
using the Kindle & prompt
2020-11-27 19:34:20 +00:00
Apprentice Harper
856fef55be Merge pull request #1268 from keshavgbpecdelhi/patch-3
changing wil to will
2020-11-27 19:34:08 +00:00
Apprentice Harper
f2fa0426b7 Merge pull request #1267 from keshavgbpecdelhi/patch-2
prompt and will
2020-11-27 19:33:59 +00:00
Apprentice Harper
c3376cc492 Merge pull request #1266 from keshavgbpecdelhi/patch-1
"promt" doesn't make any sense
2020-11-27 19:33:46 +00:00
Apprentice Harper
dc72c368a5 Update ReadMe_Overview.txt 2020-11-27 19:32:25 +00:00
Apprentice Harper
77033e1602 Update FAQs.md
update with calibre 5 and new KFX info
2020-11-27 19:29:12 +00:00
Apprentice Harper
15cd372ad9 Update README.md
Update ReadMe for calibre 5 and new KFX DRM
2020-11-27 19:20:44 +00:00
Apprentice Harper
c52e4db3df Python 3 fix for old ereader PDB DRM removal 2020-11-27 15:51:33 +00:00
Apprentice Harper
45038cc77b Python 3 fix for epubtest.py that detects version of DRM used 2020-11-27 15:49:57 +00:00
Apprentice Harper
5ec9c98a0b Python 3 fixes for Android kindle key retrieval 2020-11-27 15:46:06 +00:00
xxyzz
66bab7bd7d using byte string in kfxdedrm.py 2020-11-27 22:01:18 +08:00
Apprentice Harper
e0c7d7d382 Revert "PyCrypto requires RSA values to be long"
This reverts commit a1703e15d4.
2020-11-25 08:36:06 +00:00
keshavgbpecdelhi
971db9ae71 using the Kindle & prompt
As I already said prompt is the right word so yeah...
and "you are use kindle" is making no sense so replacing it to make it meaningful i.e. "If you are using the Kindle for PC under Wine"
2020-10-01 00:16:55 +05:30
keshavgbpecdelhi
cf829db532 wil to will
typo
2020-10-01 00:05:46 +05:30
keshavgbpecdelhi
80c8bd2d24 prompt and will
Sorry but typos are typos 
"promt" should be written as "prompt"
and "wil" should be "will"
2020-10-01 00:01:32 +05:30
keshavgbpecdelhi
969599ce6b "promt" doesn't make any sense
I think it may be a silly mistake or something because the other prompts are written well except this. Just to webpage will not look authentic by using a wrong spelling so writing the sentence like as follows :
Clicking this button will prompt you to enter a new name for the highlighted key in the list.
2020-09-30 23:12:26 +05:30
29 changed files with 431 additions and 369 deletions

39
.github/workflows/Format.yaml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Python code format
on:
push:
branches: master
jobs:
Format:
if: "contains(github.event.head_commit.message, '!format')"
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@main
- name: Set up Python
uses: actions/setup-python@main
with:
python-version: 3.x
- uses: actions/cache@main
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-format
restore-keys: |
${{ runner.os }}-pip-format
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install autopep8 pycodestyle
- name: Format by autopep8 then Push
env:
GIT_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_ACTOR: github-actions[bot]
run: |
export HASH_SHORT=$(git rev-parse --short HEAD)
git checkout -b format--${HASH_SHORT}
git config --global user.email $GIT_EMAIL
git config --global user.name $GIT_ACTOR
python -m autopep8 --in-place --aggressive --aggressive --experimental -r ./
git add -A
git commit -m 'Format by autopep8' -m From: -m $(git rev-parse HEAD)
git push --set-upstream origin format--${HASH_SHORT}

26
.github/workflows/Lint.yaml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: Python code review
on: [push, pull_request]
jobs:
Test:
runs-on: ubuntu-20.04
strategy:
fail-fast: false
steps:
- uses: actions/checkout@main
- name: Set up Python
uses: actions/setup-python@main
with:
python-version: 3.x
- uses: actions/cache@main
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-lint
restore-keys: |
${{ runner.os }}-pip-lint
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8
- name: Lint with flake8
run: |
python -m flake8 . --builtins=_,I --ignore=E501 --count --benchmark --show-source --statistics

View File

@@ -1,35 +0,0 @@
name: Python_tests
on: [push, pull_request]
jobs:
Python_tests:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [macos-latest]
python-version: [2.7, 3.8]
steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install flake8 pytest # -r requirements.txt
#- name: Check formatting with black
# if: matrix.python-version == '3.8'
# run: |
# pip install black
# black --check .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --builtins=_,I --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --builtins=_,I --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
#- name: Test with pytest
# run: pytest
#- name: Run doctests with pytest
# run: pytest --doctest-modules

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
.DS_Store

View File

@@ -38,7 +38,7 @@ li {margin-top: 0.5em}
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will promt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>

View File

@@ -46,7 +46,7 @@ li {margin-top: 0.5em}
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will promt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>
@@ -56,7 +56,7 @@ li {margin-top: 0.5em}
<p>At the bottom-left of the plugins customization dialog, you will see a button labeled "Import Existing Keyfiles". Use this button to import existing .b64 key files. Key files might come from being exported from this or older plugins, or may have been generated using the original i♥cabbages script, or you may have made it by following the instructions above.</p>
<p>Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
<p>Once done creating/deleting/renaming/importing decryption keys, click Close to exit the customization dialogue. Your changes will only be saved permanently when you click OK in the main configuration dialog.</p>
<h3>NOOK Study</h3>
<p>Books downloaded through NOOK Study may or may not use the key found using the above method. If a book is not decrypted successfully with any of the keys, the plugin will attempt to recover keys from the NOOK Study log file and use them.</p>

View File

@@ -36,7 +36,7 @@ li {margin-top: 0.5em}
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a red "X". Clicking this button will delete the highlighted Kindle serial number from the list. You will be prompted once to be sure thats what you truly mean to do. Once gone, its permanently gone.</p>
<p>Once done creating/deleting serial numbers, click Close to exit the customization dialogue. Your changes wil only be saved permanently when you click OK in the main configuration dialog.</p>
<p>Once done creating/deleting serial numbers, click Close to exit the customization dialogue. Your changes will only be saved permanently when you click OK in the main configuration dialog.</p>
</body>

View File

@@ -38,7 +38,7 @@ li {margin-top: 0.5em}
<h3>Renaming Keys:</h3>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will promt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<p>On the right-hand side of the plugins customization dialog, you will see a button with an icon that looks like a sheet of paper. Clicking this button will prompt you to enter a new name for the highlighted key in the list. Enter the new name for the encryption key and click the OK button to use the new name, or Cancel to revert to the old name..</p>
<h3>Exporting Keys:</h3>
@@ -46,7 +46,7 @@ li {margin-top: 0.5em}
<h3>Linux Users: WINEPREFIX</h3>
<p>Under the list of keys, Linux users will see a text field labeled "WINEPREFIX". If you are use Kindle for PC under Wine, and your wine installation containing Kindle for PC isn't the default Wine installation, you may enter the full path to the correct Wine installation here. Leave blank if you are unsure.</p>
<p>Under the list of keys, Linux users will see a text field labeled "WINEPREFIX". If you are using the Kindle for PC under Wine, and your wine installation containing Kindle for PC isn't the default Wine installation, you may enter the full path to the correct Wine installation here. Leave blank if you are unsure.</p>
<h3>Importing Existing Keyfiles:</h3>

View File

@@ -5,7 +5,7 @@
# Copyright © 2008-2020 Apprentice Harper et al.
__license__ = 'GPL v3'
__version__ = '7.0.0'
__version__ = '7.0.3'
__docformat__ = 'restructuredtext en'
@@ -69,14 +69,18 @@ __docformat__ = 'restructuredtext en'
# 6.6.3 - More cleanup of kindle book names and start of support for .kinf2018
# 6.7.0 - Handle new library in calibre.
# 6.8.0 - Full support for .kinf2018 and new KFX encryption (Kindle for PC/Mac 2.5+)
# 7.0.0 - Switched to Python 3 for calibre 5.0. Thanks to all who comtibuted
# 6.8.1 - Kindle key fix for Mac OS X Big Sur
# 7.0.0 - Switched to Python 3 for calibre 5.0. Thanks to all who contributed
# 7.0.1 - More Python 3 changes. Adobe PDF decryption should now work in some cases
# 7.0.2 - More Python 3 changes. Adobe PDF decryption should now work on PC too.
# 7.0.3 - More Python 3 changes. Integer division in ineptpdf.py
"""
Decrypt DRMed ebooks.
"""
PLUGIN_NAME = "DeDRM"
PLUGIN_VERSION_TUPLE = (7, 0, 0)
PLUGIN_VERSION_TUPLE = tuple([int(x) for x in __version__.split(".")])
PLUGIN_VERSION = ".".join([str(x)for x in PLUGIN_VERSION_TUPLE])
# Include an html helpfile in the plugin's zipfile with the following name.
RESOURCE_NAME = PLUGIN_NAME + '_Help.htm'
@@ -420,7 +424,7 @@ class DeDRM(FileTypePlugin):
# Attempt to decrypt epub with each encryption key (generated or provided).
print("{0} v{1}: {2} is a PDF ebook".format(PLUGIN_NAME, PLUGIN_VERSION, os.path.basename(path_to_ebook)))
for keyname, userkeyhex in dedrmprefs['adeptkeys'].items():
userkey = userkeyhex.decode('hex')
userkey = codecs.decode(userkeyhex,'hex')
print("{0} v{1}: Trying Encryption key {2:s}".format(PLUGIN_NAME, PLUGIN_VERSION, keyname))
of = self.temporary_file(".pdf")
@@ -466,7 +470,7 @@ class DeDRM(FileTypePlugin):
newkeys = []
for keyvalue in defaultkeys:
if keyvalue.encode('hex') not in dedrmprefs['adeptkeys'].values():
if codecs.encode(keyvalue,'hex') not in dedrmprefs['adeptkeys'].values():
newkeys.append(keyvalue)
if len(newkeys) > 0:
@@ -490,7 +494,7 @@ class DeDRM(FileTypePlugin):
# Store the new successful key in the defaults
print("{0} v{1}: Saving a new default key".format(PLUGIN_NAME, PLUGIN_VERSION))
try:
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',keyvalue.encode('hex'))
dedrmprefs.addnamedvaluetoprefs('adeptkeys','default_key',codecs.encode(keyvalue,'hex'))
dedrmprefs.writeprefs()
print("{0} v{1}: Saved a new default key after {2:.1f} seconds".format(PLUGIN_NAME, PLUGIN_VERSION,time.time()-self.starttime))
except:
@@ -595,7 +599,7 @@ class DeDRM(FileTypePlugin):
of = self.temporary_file(".pmlz")
# Give the userkey, ebook and TemporaryPersistent file to the decryption function.
result = erdr2pml.decryptBook(path_to_ebook, of.name, True, userkey.decode('hex'))
result = erdr2pml.decryptBook(path_to_ebook, of.name, True, codecs.decode(userkey,'hex'))
of.close()

View File

@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# androidkindlekey.py
# Copyright © 2010-20 by Thom, Apprentice et al.
# Copyright © 2010-20 by Thom, Apprentice Harper et al.
# Revision history:
# 1.0 - AmazonSecureStorage.xml decryption to serial number
@@ -30,10 +30,7 @@ import tempfile
import zlib
import tarfile
from hashlib import md5
try:
from cStringIO import StringIO
except ImportError:
from io import BytesIO as StringIO
from io import BytesIO
from binascii import a2b_hex, b2a_hex
# Routines common to Mac and PC
@@ -116,7 +113,7 @@ class AndroidObfuscation(object):
cipher = self._get_cipher()
padding = len(self.key) - len(plaintext) % len(self.key)
plaintext += chr(padding) * padding
return b2a_hex(cipher.encrypt(plaintext))
return b2a_hex(cipher.encrypt(plaintext.encode('utf-8')))
def decrypt(self, ciphertext):
cipher = self._get_cipher()
@@ -182,7 +179,7 @@ def get_serials1(path=STORAGE1):
obfuscation = AndroidObfuscation()
def get_value(key):
encrypted_key = obfuscation.encrypt(a2b_hex(key))
encrypted_key = obfuscation.encrypt(key)
encrypted_value = storage.get(encrypted_key)
if encrypted_value:
return obfuscation.decrypt(encrypted_value)
@@ -198,6 +195,7 @@ def get_serials1(path=STORAGE1):
try:
tokens = set(get_value('kindle.account.tokens').split(','))
except:
sys.stderr.write('cannot get kindle account tokens\n')
return []
serials = []
@@ -251,8 +249,8 @@ def get_serials2(path=STORAGE2):
serials = []
for x in dsns:
serials.append(x)
for y in tokens:
serials.append(x)
serials.append(y)
serials.append(x+y)
return serials
@@ -277,7 +275,7 @@ def get_serials(path=STORAGE):
read = open(path, 'rb')
head = read.read(24)
if head[:14] == b'ANDROID BACKUP':
output = StringIO(zlib.decompress(read.read()))
output = BytesIO(zlib.decompress(read.read()))
except Exception:
pass
finally:
@@ -314,7 +312,7 @@ def getkey(outfile, inpath):
if len(keys) > 0:
with open(outfile, 'w') as keyfileout:
for key in keys:
keyfileout.write(b2a_hex(key))
keyfileout.write(key)
keyfileout.write("\n")
return True
return False
@@ -391,7 +389,7 @@ def gui_main():
import tkinter.filedialog
except:
print("tkinter not installed")
return cli_main()
return 0
class DecryptionDialog(tkinter.Frame):
def __init__(self, root):

View File

@@ -175,7 +175,7 @@ class ConfigWidget(QWidget):
def load_resource(self, name):
with ZipFile(self.plugin_path, 'r') as zf:
if name in zf.namelist():
return zf.read(name)
return zf.read(name).decode('utf-8')
return ""

View File

@@ -169,12 +169,12 @@ def getfiledata(file, zi):
def encryption(infile):
# returns encryption: one of Unencrypted, Adobe, B&N and Unknown
encryption = "Unknown"
encryption = "Error When Checking."
try:
with open(infile,'rb') as infileobject:
bookdata = infileobject.read(58)
# Check for Zip
if bookdata[0:0+2] == "PK":
if bookdata[0:0+2] == b"PK":
foundrights = False
foundencryption = False
inzip = zipfile.ZipFile(infile,'r')
@@ -198,7 +198,10 @@ def encryption(infile):
def main():
argv=unicode_argv()
print(encryption(argv[1]))
if len(argv) < 2:
print("Give an ePub file as a parameter.")
else:
print(encryption(argv[1]))
return 0
if __name__ == "__main__":

View File

@@ -200,7 +200,7 @@ class Sectionizer(object):
self.num_sections, = struct.unpack('>H', self.contents[76:78])
# Dictionary or normal content (TODO: Not hard-coded)
if self.header[0x3C:0x3C+8] != ident:
if self.header[0x3C:0x3C+8] == "PDctPPrs":
if self.header[0x3C:0x3C+8] == b"PDctPPrs":
self.bkType = "Dict"
else:
raise ValueError('Invalid file format')
@@ -240,7 +240,7 @@ def sanitizeFileName(name):
def fixKey(key):
def fixByte(b):
return b ^ ((b ^ (b<<1) ^ (b<<2) ^ (b<<3) ^ (b<<4) ^ (b<<5) ^ (b<<6) ^ (b<<7) ^ 0x80) & 0x80)
return "".join([chr(fixByte(ord(a))) for a in key])
return bytes([fixByte(a) for a in key])
def deXOR(text, sp, table):
r=''
@@ -269,13 +269,13 @@ class EreaderProcessor(object):
raise ValueError('incorrect eReader version (error 2)')
input = des.decrypt(data[-cookie_size:])
def unshuff(data, shuf):
r = [''] * len(data)
r = [0] * len(data)
j = 0
for i in range(len(data)):
j = (j + shuf) % len(data)
r[j] = data[i]
assert len("".join(r)) == len(data)
return "".join(r)
assert len(bytes(r)) == len(data)
return bytes(r)
r = unshuff(input[0:-8], cookie_shuf)
drm_sub_version = struct.unpack('>H', r[0:2])[0]
@@ -354,7 +354,7 @@ class EreaderProcessor(object):
def getImage(self, i):
sect = self.section_reader(self.first_image_page + i)
name = sect[4:4+32].strip('\0')
name = sect[4:4+32].strip(b'\0')
data = sect[62:]
return sanitizeFileName(name.decode('windows-1252')), data
@@ -404,7 +404,7 @@ class EreaderProcessor(object):
def getText(self):
des = Des(fixKey(self.content_key))
r = ''
r = b''
for i in range(self.num_text_pages):
logging.debug('get page %d', i)
r += zlib.decompress(des.decrypt(self.section_reader(1 + i)))
@@ -456,8 +456,7 @@ def cleanPML(pml):
# Convert special characters to proper PML code. High ASCII start at (\x80, \a128) and go up to (\xff, \a255)
pml2 = pml
for k in range(128,256):
badChar = chr(k)
pml2 = pml2.replace(badChar, '\\a%03d' % k)
pml2 = pml2.replace(bytes([k]), b'\\a%03d' % k)
return pml2
def decryptBook(infile, outpath, make_pmlz, user_key):
@@ -476,7 +475,7 @@ def decryptBook(infile, outpath, make_pmlz, user_key):
if not os.path.exists(outdir):
os.makedirs(outdir)
print("Decoding File")
sect =Sectionizer(infile, 'PNRdPPrs')
sect =Sectionizer(infile, b'PNRdPPrs')
er = EreaderProcessor(sect, user_key)
if er.getNumImages() > 0:

View File

@@ -35,6 +35,7 @@ __version__ = "5.0"
import sys
import os
import traceback
import base64
import zlib
import zipfile
from zipfile import ZipInfo, ZipFile, ZIP_STORED, ZIP_DEFLATED
@@ -151,7 +152,7 @@ def _load_crypto_libcrypto():
def decrypt(self, data):
out = create_string_buffer(len(data))
iv = ("\x00" * self._blocksize)
iv = (b'\x00' * self._blocksize)
rv = AES_cbc_encrypt(data, out, len(data), self._key, iv, 0)
if rv == 0:
raise IGNOBLEError('AES decryption failed')
@@ -164,7 +165,7 @@ def _load_crypto_pycrypto():
class AES(object):
def __init__(self, key):
self._aes = _AES.new(key, _AES.MODE_CBC, '\x00'*16)
self._aes = _AES.new(key, _AES.MODE_CBC, b'\x00'*16)
def decrypt(self, data):
return self._aes.decrypt(data)
@@ -207,15 +208,15 @@ class Decryptor(object):
def decompress(self, bytes):
dc = zlib.decompressobj(-15)
bytes = dc.decompress(bytes)
ex = dc.decompress('Z') + dc.flush()
ex = dc.decompress(b'Z') + dc.flush()
if ex:
bytes = bytes + ex
return bytes
def decrypt(self, path, data):
if path in self._encrypted:
if bytes(path,'utf-8') in self._encrypted:
data = self._aes.decrypt(data)[16:]
data = data[:-ord(data[-1])]
data = data[:-data[-1]]
data = self.decompress(data)
return data
@@ -241,7 +242,7 @@ def ignobleBook(inpath):
def decryptBook(keyb64, inpath, outpath):
if AES is None:
raise IGNOBLEError("PyCrypto or OpenSSL must be installed.")
key = keyb64.decode('base64')[:16]
key = base64.b64decode(keyb64)[:16]
aes = AES(key)
with closing(ZipFile(open(inpath, 'rb'))) as inf:
namelist = set(inf.namelist())
@@ -259,8 +260,8 @@ def decryptBook(keyb64, inpath, outpath):
if len(bookkey) != 64:
print("{0:s} is not a secure Barnes & Noble ePub.".format(os.path.basename(inpath)))
return 1
bookkey = aes.decrypt(bookkey.decode('base64'))
bookkey = bookkey[:-ord(bookkey[-1])]
bookkey = aes.decrypt(base64.b64decode(bookkey))
bookkey = bookkey[:-bookkey[-1]]
encryption = inf.read('META-INF/encryption.xml')
decryptor = Decryptor(bookkey[-16:], encryption)
kwds = dict(compression=ZIP_DEFLATED, allowZip64=False)

18
DeDRM_plugin/ineptepub.py Executable file → Normal file
View File

@@ -311,7 +311,7 @@ def _load_crypto_pycrypto():
total = 0
for byte in bytes:
total = (total << 8) + byte
return long(total)
return total
def decrypt(self, data):
return _PKCS1_v1_5.new(self._rsa).decrypt(data, 172)
@@ -353,12 +353,16 @@ class Decryptor(object):
def decompress(self, bytes):
dc = zlib.decompressobj(-15)
bytes = dc.decompress(bytes)
ex = dc.decompress(b'Z') + dc.flush()
if ex:
bytes = bytes + ex
return bytes
try:
decompressed_bytes = dc.decompress(bytes)
ex = dc.decompress(b'Z') + dc.flush()
if ex:
decompressed_bytes = decompressed_bytes + ex
except:
# possibly not compressed by zip - just return bytes
return bytes
return decompressed_bytes
def decrypt(self, path, data):
if path.encode('utf-8') in self._encrypted:
data = self._aes.decrypt(data)[16:]

366
DeDRM_plugin/ineptpdf.py Normal file → Executable file
View File

@@ -54,12 +54,14 @@ Decrypts Adobe ADEPT-encrypted PDF files.
__license__ = 'GPL v3'
__version__ = "9.0.0"
import codecs
import sys
import os
import re
import zlib
import struct
import hashlib
from io import BytesIO
from decimal import Decimal
import itertools
import xml.etree.ElementTree as etree
@@ -258,7 +260,8 @@ def _load_crypto_pycrypto():
from Crypto.PublicKey import RSA as _RSA
from Crypto.Cipher import ARC4 as _ARC4
from Crypto.Cipher import AES as _AES
from Crypto.Cipher import PKCS1_v1_5 as _PKCS1_v1_5
# ASN.1 parsing code from tlslite
class ASN1Error(Exception):
pass
@@ -372,7 +375,7 @@ def _load_crypto_pycrypto():
class RSA(object):
def __init__(self, der):
key = ASN1Parser([ord(x) for x in der])
key = ASN1Parser([x for x in der])
key = [key.getChild(x).value for x in range(1, 4)]
key = [self.bytesToNumber(v) for v in key]
self._rsa = _RSA.construct(key)
@@ -384,7 +387,7 @@ def _load_crypto_pycrypto():
return total
def decrypt(self, data):
return self._rsa.decrypt(data)
return _PKCS1_v1_5.new(self._rsa).decrypt(data, 172)
return (ARC4, RSA, AES)
@@ -403,7 +406,6 @@ def _load_crypto():
ARC4, RSA, AES = _load_crypto()
from io import BytesIO
# Do we generate cross reference streams on output?
@@ -472,16 +474,16 @@ class PSLiteral(PSObject):
Use PSLiteralTable.intern() instead.
'''
def __init__(self, name):
self.name = name
self.name = name.decode('utf-8')
return
def __repr__(self):
name = []
for char in self.name:
if not char.isalnum():
char = b'#%02x' % ord(char)
char = '#%02x' % ord(char)
name.append(char)
return b'/%s' % ''.join(name)
return '/%s' % ''.join(name)
# PSKeyword
class PSKeyword(PSObject):
@@ -491,7 +493,7 @@ class PSKeyword(PSObject):
Use PSKeywordTable.intern() instead.
'''
def __init__(self, name):
self.name = name
self.name = name.decode('utf-8')
return
def __repr__(self):
@@ -521,12 +523,12 @@ PSLiteralTable = PSSymbolTable(PSLiteral)
PSKeywordTable = PSSymbolTable(PSKeyword)
LIT = PSLiteralTable.intern
KWD = PSKeywordTable.intern
KEYWORD_BRACE_BEGIN = KWD('{')
KEYWORD_BRACE_END = KWD('}')
KEYWORD_ARRAY_BEGIN = KWD('[')
KEYWORD_ARRAY_END = KWD(']')
KEYWORD_DICT_BEGIN = KWD('<<')
KEYWORD_DICT_END = KWD('>>')
KEYWORD_BRACE_BEGIN = KWD(b'{')
KEYWORD_BRACE_END = KWD(b'}')
KEYWORD_ARRAY_BEGIN = KWD(b'[')
KEYWORD_ARRAY_END = KWD(b']')
KEYWORD_DICT_BEGIN = KWD(b'<<')
KEYWORD_DICT_END = KWD(b'>>')
def literal_name(x):
@@ -548,18 +550,18 @@ def keyword_name(x):
## PSBaseParser
##
EOL = re.compile(r'[\r\n]')
SPC = re.compile(r'\s')
NONSPC = re.compile(r'\S')
HEX = re.compile(r'[0-9a-fA-F]')
END_LITERAL = re.compile(r'[#/%\[\]()<>{}\s]')
END_HEX_STRING = re.compile(r'[^\s0-9a-fA-F]')
HEX_PAIR = re.compile(r'[0-9a-fA-F]{2}|.')
END_NUMBER = re.compile(r'[^0-9]')
END_KEYWORD = re.compile(r'[#/%\[\]()<>{}\s]')
END_STRING = re.compile(r'[()\134]')
OCT_STRING = re.compile(r'[0-7]')
ESC_STRING = { 'b':8, 't':9, 'n':10, 'f':12, 'r':13, '(':40, ')':41, '\\':92 }
EOL = re.compile(rb'[\r\n]')
SPC = re.compile(rb'\s')
NONSPC = re.compile(rb'\S')
HEX = re.compile(rb'[0-9a-fA-F]')
END_LITERAL = re.compile(rb'[#/%\[\]()<>{}\s]')
END_HEX_STRING = re.compile(rb'[^\s0-9a-fA-F]')
HEX_PAIR = re.compile(rb'[0-9a-fA-F]{2}|.')
END_NUMBER = re.compile(rb'[^0-9]')
END_KEYWORD = re.compile(rb'[#/%\[\]()<>{}\s]')
END_STRING = re.compile(rb'[()\\]')
OCT_STRING = re.compile(rb'[0-7]')
ESC_STRING = { b'b':8, b't':9, b'n':10, b'f':12, b'r':13, b'(':40, b')':41, b'\\':92 }
class PSBaseParser(object):
@@ -591,7 +593,6 @@ class PSBaseParser(object):
if not pos:
pos = self.bufpos+self.charpos
self.fp.seek(pos)
##print >>sys.stderr, 'poll(%d): %r' % (pos, self.fp.read(n))
self.fp.seek(pos0)
return
@@ -602,7 +603,7 @@ class PSBaseParser(object):
self.fp.seek(pos)
# reset the status for nextline()
self.bufpos = pos
self.buf = ''
self.buf = b''
self.charpos = 0
# reset the status for nexttoken()
self.parse1 = self.parse_main
@@ -624,32 +625,32 @@ class PSBaseParser(object):
if not m:
return (self.parse_main, len(s))
j = m.start(0)
c = s[j]
c = bytes([s[j]])
self.tokenstart = self.bufpos+j
if c == '%':
self.token = '%'
if c == b'%':
self.token = c
return (self.parse_comment, j+1)
if c == '/':
self.token = ''
if c == b'/':
self.token = b''
return (self.parse_literal, j+1)
if c in '-+' or c.isdigit():
if c in b'-+' or c.isdigit():
self.token = c
return (self.parse_number, j+1)
if c == '.':
if c == b'.':
self.token = c
return (self.parse_decimal, j+1)
if c.isalpha():
self.token = c
return (self.parse_keyword, j+1)
if c == '(':
self.token = ''
if c == b'(':
self.token = b''
self.paren = 1
return (self.parse_string, j+1)
if c == '<':
self.token = ''
if c == b'<':
self.token = b''
return (self.parse_wopen, j+1)
if c == '>':
self.token = ''
if c == b'>':
self.token = b''
return (self.parse_wclose, j+1)
self.add_token(KWD(c))
return (self.parse_main, j+1)
@@ -676,20 +677,20 @@ class PSBaseParser(object):
return (self.parse_literal, len(s))
j = m.start(0)
self.token += s[i:j]
c = s[j]
if c == '#':
self.hex = ''
c = bytes([s[j]])
if c == b'#':
self.hex = b''
return (self.parse_literal_hex, j+1)
self.add_token(LIT(self.token))
return (self.parse_main, j)
def parse_literal_hex(self, s, i):
c = s[i]
c = bytes([s[i]])
if HEX.match(c) and len(self.hex) < 2:
self.hex += c
return (self.parse_literal_hex, i+1)
if self.hex:
self.token += chr(int(self.hex, 16))
self.token += bytes([int(self.hex, 16)])
return (self.parse_literal, i)
def parse_number(self, s, i):
@@ -699,8 +700,8 @@ class PSBaseParser(object):
return (self.parse_number, len(s))
j = m.start(0)
self.token += s[i:j]
c = s[j]
if c == '.':
c = bytes([s[j]])
if c == b'.':
self.token += c
return (self.parse_decimal, j+1)
try:
@@ -716,7 +717,7 @@ class PSBaseParser(object):
return (self.parse_decimal, len(s))
j = m.start(0)
self.token += s[i:j]
self.add_token(Decimal(self.token))
self.add_token(Decimal(self.token.decode('utf-8')))
return (self.parse_main, j)
def parse_keyword(self, s, i):
@@ -742,58 +743,59 @@ class PSBaseParser(object):
return (self.parse_string, len(s))
j = m.start(0)
self.token += s[i:j]
c = s[j]
if c == '\\':
c = bytes([s[j]])
if c == b'\\':
self.oct = ''
return (self.parse_string_1, j+1)
if c == '(':
if c == b'(':
self.paren += 1
self.token += c
return (self.parse_string, j+1)
if c == ')':
if c == b')':
self.paren -= 1
if self.paren:
self.token += c
return (self.parse_string, j+1)
self.add_token(self.token)
return (self.parse_main, j+1)
def parse_string_1(self, s, i):
c = s[i]
c = bytes([s[i]])
if OCT_STRING.match(c) and len(self.oct) < 3:
self.oct += c
return (self.parse_string_1, i+1)
if self.oct:
self.token += chr(int(self.oct, 8))
self.token += bytes([int(self.oct, 8)])
return (self.parse_string, i)
if c in ESC_STRING:
self.token += chr(ESC_STRING[c])
self.token += bytes([ESC_STRING[c]])
return (self.parse_string, i+1)
def parse_wopen(self, s, i):
c = s[i]
c = bytes([s[i]])
if c.isspace() or HEX.match(c):
return (self.parse_hexstring, i)
if c == '<':
if c == b'<':
self.add_token(KEYWORD_DICT_BEGIN)
i += 1
return (self.parse_main, i)
def parse_wclose(self, s, i):
c = s[i]
if c == '>':
c = bytes([s[i]])
if c == b'>':
self.add_token(KEYWORD_DICT_END)
i += 1
return (self.parse_main, i)
def parse_hexstring(self, s, i):
m = END_HEX_STRING.search(s, i)
if not m:
m1 = END_HEX_STRING.search(s, i)
if not m1:
self.token += s[i:]
return (self.parse_hexstring, len(s))
j = m.start(0)
j = m1.start(0)
self.token += s[i:j]
token = HEX_PAIR.sub(lambda m: chr(int(m.group(0), 16)),
SPC.sub('', self.token))
token = HEX_PAIR.sub(lambda m2: bytes([int(m2.group(0), 16)]),
SPC.sub(b'', self.token))
self.add_token(token)
return (self.parse_main, j)
@@ -808,15 +810,15 @@ class PSBaseParser(object):
'''
Fetches a next line that ends either with \\r or \\n.
'''
linebuf = ''
linebuf = b''
linepos = self.bufpos + self.charpos
eol = False
while 1:
self.fillbuf()
if eol:
c = self.buf[self.charpos]
c = bytes([self.buf[self.charpos]])
# handle '\r\n'
if c == '\n':
if c == b'\n':
linebuf += c
self.charpos += 1
break
@@ -824,7 +826,7 @@ class PSBaseParser(object):
if m:
linebuf += self.buf[self.charpos:m.end(0)]
self.charpos = m.end(0)
if linebuf[-1] == '\r':
if bytes([linebuf[-1]]) == b'\r':
eol = True
else:
break
@@ -840,7 +842,7 @@ class PSBaseParser(object):
'''
self.fp.seek(0, 2)
pos = self.fp.tell()
buf = ''
buf = b''
while 0 < pos:
prevpos = pos
pos = max(0, pos-self.BUFSIZ)
@@ -848,13 +850,13 @@ class PSBaseParser(object):
s = self.fp.read(prevpos-pos)
if not s: break
while 1:
n = max(s.rfind('\r'), s.rfind('\n'))
n = max(s.rfind(b'\r'), s.rfind(b'\n'))
if n == -1:
buf = s + buf
break
yield s[n:]+buf
s = s[:n]
buf = ''
buf = b''
return
@@ -910,17 +912,17 @@ class PSStackParser(PSBaseParser):
def nextobject(self, direct=False):
'''
Yields a list of objects: keywords, literals, strings,
Yields a list of objects: keywords, literals, strings (byte arrays),
numbers, arrays and dictionaries. Arrays and dictionaries
are represented as Python sequence and dictionaries.
'''
while not self.results:
(pos, token) = self.nexttoken()
##print (pos,token), (self.curtype, self.curstack)
if (isinstance(token, int) or
isinstance(token, Decimal) or
isinstance(token, bool) or
isinstance(token, str) or
isinstance(token, bytearray) or
isinstance(token, bytes) or
isinstance(token, PSLiteral)):
# normal token
self.push((pos, token))
@@ -963,10 +965,10 @@ class PSStackParser(PSBaseParser):
return obj
LITERAL_CRYPT = PSLiteralTable.intern('Crypt')
LITERALS_FLATE_DECODE = (PSLiteralTable.intern('FlateDecode'), PSLiteralTable.intern('Fl'))
LITERALS_LZW_DECODE = (PSLiteralTable.intern('LZWDecode'), PSLiteralTable.intern('LZW'))
LITERALS_ASCII85_DECODE = (PSLiteralTable.intern('ASCII85Decode'), PSLiteralTable.intern('A85'))
LITERAL_CRYPT = LIT(b'Crypt')
LITERALS_FLATE_DECODE = (LIT(b'FlateDecode'), LIT(b'Fl'))
LITERALS_LZW_DECODE = (LIT(b'LZWDecode'), LIT(b'LZW'))
LITERALS_ASCII85_DECODE = (LIT(b'ASCII85Decode'), LIT(b'A85'))
## PDF Objects
@@ -1020,7 +1022,7 @@ def resolve_all(x):
if isinstance(x, list):
x = [ resolve_all(v) for v in x ]
elif isinstance(x, dict):
for (k,v) in x.iteritems():
for (k,v) in iter(x.items()):
x[k] = resolve_all(v)
return x
@@ -1028,13 +1030,13 @@ def decipher_all(decipher, objid, genno, x):
'''
Recursively decipher X.
'''
if isinstance(x, str):
if isinstance(x, bytearray) or isinstance(x,bytes):
return decipher(objid, genno, x)
decf = lambda v: decipher_all(decipher, objid, genno, v)
if isinstance(x, list):
x = [decf(v) for v in x]
elif isinstance(x, dict):
x = dict((k, decf(v)) for (k, v) in x.iteritems())
x = dict((k, decf(v)) for (k, v) in iter(x.items()))
return x
@@ -1065,7 +1067,7 @@ def num_value(x):
def str_value(x):
x = resolve1(x)
if not isinstance(x, str):
if not (isinstance(x, bytearray) or isinstance(x, bytes)):
if STRICT:
raise PDFTypeError('String required: %r' % x)
return ''
@@ -1166,7 +1168,6 @@ class PDFStream(PDFObject):
if 'Filter' not in self.dic:
self.data = data
self.rawdata = None
##print self.dict
return
filters = self.dic['Filter']
if not isinstance(filters, list):
@@ -1176,7 +1177,7 @@ class PDFStream(PDFObject):
# will get errors if the document is encrypted.
data = zlib.decompress(data)
elif f in LITERALS_LZW_DECODE:
data = ''.join(LZWDecoder(BytesIO(data)).run())
data = b''.join(LZWDecoder(BytesIO(data)).run())
elif f in LITERALS_ASCII85_DECODE:
data = ascii85decode(data)
elif f == LITERAL_CRYPT:
@@ -1204,7 +1205,7 @@ class PDFStream(PDFObject):
pred = data[i]
ent1 = data[i+1:i+1+columns]
if pred == b'\x02':
ent1 = ''.join(bytes([(a+b) & 255]) \
ent1 = b''.join(bytes([(a+b) & 255]) \
for (a,b) in zip(ent0,ent1))
buf += ent1
ent0 = ent1
@@ -1239,11 +1240,11 @@ class PDFEncryptionError(PDFException): pass
class PDFPasswordIncorrect(PDFEncryptionError): pass
# some predefined literals and keywords.
LITERAL_OBJSTM = PSLiteralTable.intern('ObjStm')
LITERAL_XREF = PSLiteralTable.intern('XRef')
LITERAL_PAGE = PSLiteralTable.intern('Page')
LITERAL_PAGES = PSLiteralTable.intern('Pages')
LITERAL_CATALOG = PSLiteralTable.intern('Catalog')
LITERAL_OBJSTM = LIT(b'ObjStm')
LITERAL_XREF = LIT(b'XRef')
LITERAL_PAGE = LIT(b'Page')
LITERAL_PAGES = LIT(b'Pages')
LITERAL_CATALOG = LIT(b'Catalog')
## XRefs
@@ -1261,7 +1262,7 @@ class PDFXRef(object):
return '<PDFXRef: objs=%d>' % len(self.offsets)
def objids(self):
return self.offsets.iterkeys()
return iter(self.offsets.keys())
def load(self, parser):
self.offsets = {}
@@ -1272,10 +1273,10 @@ class PDFXRef(object):
raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
if not line:
raise PDFNoValidXRef('Premature eof: %r' % parser)
if line.startswith('trailer'):
if line.startswith(b'trailer'):
parser.seek(pos)
break
f = line.strip().split(' ')
f = line.strip().split(b' ')
if len(f) != 2:
raise PDFNoValidXRef('Trailer not found: %r: line=%r' % (parser, line))
try:
@@ -1287,16 +1288,17 @@ class PDFXRef(object):
(_, line) = parser.nextline()
except PSEOF:
raise PDFNoValidXRef('Unexpected EOF - file corrupted?')
f = line.strip().split(' ')
f = line.strip().split(b' ')
if len(f) != 3:
raise PDFNoValidXRef('Invalid XRef format: %r, line=%r' % (parser, line))
(pos, genno, use) = f
if use != 'n': continue
self.offsets[objid] = (int(genno), int(pos))
if use != b'n':
continue
self.offsets[objid] = (int(genno.decode('utf-8')), int(pos.decode('utf-8')))
self.load_trailer(parser)
return
KEYWORD_TRAILER = PSKeywordTable.intern('trailer')
KEYWORD_TRAILER = KWD(b'trailer')
def load_trailer(self, parser):
try:
(_,kwd) = parser.nexttoken()
@@ -1401,7 +1403,8 @@ class PDFDocument(object):
# set_parser(parser)
# Associates the document with an (already initialized) parser object.
def set_parser(self, parser):
if self.parser: return
if self.parser:
return
self.parser = parser
# The document is set to be temporarily ready during collecting
# all the basic information about the document, e.g.
@@ -1423,13 +1426,13 @@ class PDFDocument(object):
dict_value(trailer['Encrypt']))
# fix for bad files
except:
self.encryption = ('ffffffffffffffffffffffffffffffffffff',
self.encryption = (b'ffffffffffffffffffffffffffffffffffff',
dict_value(trailer['Encrypt']))
if 'Root' in trailer:
self.set_root(dict_value(trailer['Root']))
break
else:
raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
else:
raise PDFSyntaxError('No /Root object! - Is this really a PDF?')
# The document is set to be non-ready again, until all the
# proper initialization (asking the password key and
# verifying the access permission, so on) is finished.
@@ -1450,7 +1453,7 @@ class PDFDocument(object):
# Perform the initialization with a given password.
# This step is mandatory even if there's no password associated
# with the document.
def initialize(self, password=''):
def initialize(self, password=b''):
if not self.encryption:
self.is_printable = self.is_modifiable = self.is_extractable = True
self.ready = True
@@ -1477,14 +1480,14 @@ class PDFDocument(object):
def genkey_adobe_ps(self, param):
# nice little offline principal keys dictionary
# global static principal key for German Onleihe / Bibliothek Digital
principalkeys = { 'bibliothek-digital.de': 'rRwGv2tbpKov1krvv7PO0ws9S436/lArPlfipz5Pqhw='.decode('base64')}
principalkeys = { b'bibliothek-digital.de': codecs.decode(b'rRwGv2tbpKov1krvv7PO0ws9S436/lArPlfipz5Pqhw=','base64')}
self.is_printable = self.is_modifiable = self.is_extractable = True
length = int_value(param.get('Length', 0)) / 8
length = int_value(param.get('Length', 0)) // 8
edcdata = str_value(param.get('EDCData')).decode('base64')
pdrllic = str_value(param.get('PDRLLic')).decode('base64')
pdrlpol = str_value(param.get('PDRLPol')).decode('base64')
edclist = []
for pair in edcdata.split('\n'):
for pair in edcdata.split(b'\n'):
edclist.append(pair)
# principal key request
for key in principalkeys:
@@ -1493,9 +1496,9 @@ class PDFDocument(object):
else:
raise ADEPTError('Cannot find principal key for this pdf')
shakey = SHA256(principalkey)
ivector = 16 * chr(0)
ivector = bytes(16) # 16 zero bytes
plaintext = AES.new(shakey,AES.MODE_CBC,ivector).decrypt(edclist[9].decode('base64'))
if plaintext[-16:] != 16 * chr(16):
if plaintext[-16:] != bytearray(b'\0x10')*16:
raise ADEPTError('Offlinekey cannot be decrypted, aborting ...')
pdrlpol = AES.new(plaintext[16:32],AES.MODE_CBC,edclist[2].decode('base64')).decrypt(pdrlpol)
if pdrlpol[-1] < 1 or pdrlpol[-1] > 16:
@@ -1505,8 +1508,8 @@ class PDFDocument(object):
pdrlpol = pdrlpol[:cutter]
return plaintext[:16]
PASSWORD_PADDING = '(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
'\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
PASSWORD_PADDING = b'(\xbfN^Nu\x8aAd\x00NV\xff\xfa\x01\x08..' \
b'\x00\xb6\xd0h>\x80/\x0c\xa9\xfedSiz'
# experimental aes pw support
def initialize_standard(self, password, docid, param):
# copy from a global variable
@@ -1523,7 +1526,7 @@ class PDFDocument(object):
try:
EncMetadata = str_value(param['EncryptMetadata'])
except:
EncMetadata = 'True'
EncMetadata = b'True'
self.is_printable = bool(P & 4)
self.is_modifiable = bool(P & 8)
self.is_extractable = bool(P & 16)
@@ -1540,12 +1543,12 @@ class PDFDocument(object):
hash.update(docid[0]) # 5
# aes special handling if metadata isn't encrypted
if EncMetadata == ('False' or 'false'):
hash.update('ffffffff'.decode('hex'))
hash.update(codecs.decode(b'ffffffff','hex'))
if 5 <= R:
# 8
for _ in range(50):
hash = hashlib.md5(hash.digest()[:length/8])
key = hash.digest()[:length/8]
hash = hashlib.md5(hash.digest()[:length//8])
key = hash.digest()[:length//8]
if R == 2:
# Algorithm 3.4
u1 = ARC4.new(key).decrypt(password)
@@ -1555,7 +1558,7 @@ class PDFDocument(object):
hash.update(docid[0]) # 3
x = ARC4.new(key).decrypt(hash.digest()[:16]) # 4
for i in range(1,19+1):
k = ''.join(bytes([c ^ i]) for c in key )
k = b''.join(bytes([c ^ i]) for c in key )
x = ARC4.new(k).decrypt(x)
u1 = x+x # 32bytes total
if R == 2:
@@ -1587,17 +1590,20 @@ class PDFDocument(object):
def initialize_ebx(self, password, docid, param):
self.is_printable = self.is_modifiable = self.is_extractable = True
rsa = RSA(password)
length = int_value(param.get('Length', 0)) / 8
rights = str_value(param.get('ADEPT_LICENSE')).decode('base64')
length = int_value(param.get('Length', 0)) // 8
rights = codecs.decode(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 = codecs.decode(''.join(rights.findtext(expr)).encode('utf-8'),'base64')
bookkey = rsa.decrypt(bookkey)
if bookkey[0] != '\x02':
raise ADEPTError('error decrypting book session key')
index = bookkey.index('\0') + 1
bookkey = bookkey[index:]
#if bookkey[0] != 2:
# raise ADEPTError('error decrypting book session key')
try:
index = bookkey.index(b'\0') + 1
bookkey = bookkey[index:]
except ValueError:
pass
ebx_V = int_value(param.get('V', 4))
ebx_type = int_value(param.get('EBX_ENCRYPTIONTYPE', 6))
# added because of improper booktype / decryption book session key errors
@@ -1643,7 +1649,7 @@ class PDFDocument(object):
objid = struct.pack('<L', objid ^ 0x3569ac)
genno = struct.pack('<L', genno ^ 0xca96)
key = self.decrypt_key
key += objid[0] + genno[0] + objid[1] + genno[1] + objid[2] + 'sAlT'
key += objid[0] + genno[0] + objid[1] + genno[1] + objid[2] + b'sAlT'
hash = hashlib.md5(key)
key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
return key
@@ -1652,7 +1658,7 @@ class PDFDocument(object):
def genkey_v4(self, objid, genno):
objid = struct.pack('<L', objid)[:3]
genno = struct.pack('<L', genno)[:2]
key = self.decrypt_key + objid + genno + 'sAlT'
key = self.decrypt_key + objid + genno + b'sAlT'
hash = hashlib.md5(key)
key = hash.digest()[:min(len(self.decrypt_key) + 5, 16)]
return key
@@ -1664,7 +1670,6 @@ class PDFDocument(object):
plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
# remove pkcs#5 aes padding
cutter = -1 * plaintext[-1]
#print cutter
plaintext = plaintext[:cutter]
return plaintext
@@ -1675,7 +1680,6 @@ class PDFDocument(object):
plaintext = AES.new(key,AES.MODE_CBC,ivector).decrypt(data)
# remove pkcs#5 aes padding
cutter = -1 * plaintext[-1]
#print cutter
plaintext = plaintext[:cutter]
return plaintext
@@ -1684,7 +1688,7 @@ class PDFDocument(object):
return ARC4.new(key).decrypt(data)
KEYWORD_OBJ = PSKeywordTable.intern('obj')
KEYWORD_OBJ = KWD(b'obj')
def getobj(self, objid):
if not self.ready:
@@ -1791,11 +1795,11 @@ class PDFParser(PSStackParser):
def __repr__(self):
return '<PDFParser>'
KEYWORD_R = PSKeywordTable.intern('R')
KEYWORD_ENDOBJ = PSKeywordTable.intern('endobj')
KEYWORD_STREAM = PSKeywordTable.intern('stream')
KEYWORD_XREF = PSKeywordTable.intern('xref')
KEYWORD_STARTXREF = PSKeywordTable.intern('startxref')
KEYWORD_R = KWD(b'R')
KEYWORD_ENDOBJ = KWD(b'endobj')
KEYWORD_STREAM = KWD(b'stream')
KEYWORD_XREF = KWD(b'xref')
KEYWORD_STARTXREF = KWD(b'startxref')
def do_keyword(self, pos, token):
if token in (self.KEYWORD_XREF, self.KEYWORD_STARTXREF):
self.add_results(*self.pop(1))
@@ -1843,8 +1847,8 @@ class PDFParser(PSStackParser):
if STRICT:
raise PDFSyntaxError('Unexpected EOF')
break
if 'endstream' in line:
i = line.index('endstream')
if b'endstream' in line:
i = line.index(b'endstream')
objlen += i
data += line[:i]
break
@@ -1864,7 +1868,7 @@ class PDFParser(PSStackParser):
prev = None
for line in self.revreadlines():
line = line.strip()
if line == 'startxref': break
if line == b'startxref': break
if line:
prev = line
else:
@@ -1916,7 +1920,7 @@ class PDFParser(PSStackParser):
except PDFNoValidXRef:
# fallback
self.seek(0)
pat = re.compile(r'^(\d+)\s+(\d+)\s+obj\b')
pat = re.compile(rb'^(\d+)\s+(\d+)\s+obj\b')
offsets = {}
xref = PDFXRef()
while 1:
@@ -1924,7 +1928,7 @@ class PDFParser(PSStackParser):
(pos, line) = self.nextline()
except PSEOF:
break
if line.startswith('trailer'):
if line.startswith(b'trailer'):
trailerpos = pos # remember last trailer
m = pat.match(line)
if not m: continue
@@ -1951,7 +1955,7 @@ class PDFObjStrmParser(PDFParser):
self.add_results(*self.popall())
return
KEYWORD_R = KWD('R')
KEYWORD_R = KWD(b'R')
def do_keyword(self, pos, token):
if token is self.KEYWORD_R:
# reference to indirect object
@@ -1994,7 +1998,7 @@ class PDFSerializer(object):
def dump(self, outf):
self.outf = outf
self.write(self.version)
self.write('\n%\xe2\xe3\xcf\xd3\n')
self.write(b'\n%\xe2\xe3\xcf\xd3\n')
doc = self.doc
objids = self.objids
xrefs = {}
@@ -2016,18 +2020,18 @@ class PDFSerializer(object):
startxref = self.tell()
if not gen_xref_stm:
self.write('xref\n')
self.write('0 %d\n' % (maxobj + 1,))
self.write(b'xref\n')
self.write(b'0 %d\n' % (maxobj + 1,))
for objid in range(0, maxobj + 1):
if objid in xrefs:
# force the genno to be 0
self.write("%010d 00000 n \n" % xrefs[objid][0])
self.write(b"%010d 00000 n \n" % xrefs[objid][0])
else:
self.write("%010d %05d f \n" % (0, 65535))
self.write(b"%010d %05d f \n" % (0, 65535))
self.write('trailer\n')
self.write(b'trailer\n')
self.serialize_object(trailer)
self.write('\nstartxref\n%d\n%%%%EOF' % startxref)
self.write(b'\nstartxref\n%d\n%%%%EOF' % startxref)
else: # Generate crossref stream.
@@ -2076,7 +2080,7 @@ class PDFSerializer(object):
data.append(struct.pack('>L', f2)[-fl2:])
data.append(struct.pack('>L', f3)[-fl3:])
index.extend((first, prev - first + 1))
data = zlib.compress(''.join(data))
data = zlib.compress(b''.join(data))
dic = {'Type': LITERAL_XREF, 'Size': prev + 1, 'Index': index,
'W': [1, fl2, fl3], 'Length': len(data),
'Filter': LITERALS_FLATE_DECODE[0],
@@ -2085,7 +2089,7 @@ class PDFSerializer(object):
dic['Info'] = trailer['Info']
xrefstm = PDFStream(dic, data)
self.serialize_indirect(maxobj, xrefstm)
self.write('startxref\n%d\n%%%%EOF' % startxref)
self.write(b'startxref\n%d\n%%%%EOF' % startxref)
def write(self, data):
self.outf.write(data)
self.last = data[-1:]
@@ -2094,13 +2098,10 @@ class PDFSerializer(object):
return self.outf.tell()
def escape_string(self, string):
string = string.replace('\\', '\\\\')
string = string.replace('\n', r'\n')
string = string.replace('(', r'\(')
string = string.replace(')', r'\)')
# get rid of ciando id
regularexp = re.compile(r'http://www.ciando.com/index.cfm/intRefererID/\d{5}')
if regularexp.match(string): return ('http://www.ciando.com')
string = string.replace(b'\\', b'\\\\')
string = string.replace(b'\n', rb'\n')
string = string.replace(b'(', rb'\(')
string = string.replace(b')', rb'\)')
return string
def serialize_object(self, obj):
@@ -2111,34 +2112,38 @@ class PDFSerializer(object):
obj['Subtype'] = obj['Type']
del obj['Type']
# end - hope this doesn't have bad effects
self.write('<<')
self.write(b'<<')
for key, val in obj.items():
self.write('/%s' % key)
self.write(str(LIT(key.encode('utf-8'))).encode('utf-8'))
self.serialize_object(val)
self.write('>>')
self.write(b'>>')
elif isinstance(obj, list):
self.write('[')
self.write(b'[')
for val in obj:
self.serialize_object(val)
self.write(']')
self.write(b']')
elif isinstance(obj, bytearray):
self.write(b'(%s)' % self.escape_string(obj))
elif isinstance(obj, bytes):
self.write(b'(%s)' % self.escape_string(obj))
elif isinstance(obj, str):
self.write('(%s)' % self.escape_string(obj))
self.write(b'(%s)' % self.escape_string(obj.encode('utf-8')))
elif isinstance(obj, bool):
if self.last.isalnum():
self.write(' ')
self.write(str(obj).lower())
self.write(b' ')
self.write(str(obj).lower().encode('utf-8'))
elif isinstance(obj, int):
if self.last.isalnum():
self.write(' ')
self.write(str(obj))
self.write(b' ')
self.write(str(obj).encode('utf-8'))
elif isinstance(obj, Decimal):
if self.last.isalnum():
self.write(' ')
self.write(str(obj))
self.write(b' ')
self.write(str(obj).encode('utf-8'))
elif isinstance(obj, PDFObjRef):
if self.last.isalnum():
self.write(' ')
self.write('%d %d R' % (obj.objid, 0))
self.write(b' ')
self.write(b'%d %d R' % (obj.objid, 0))
elif isinstance(obj, PDFStream):
### If we don't generate cross ref streams the object streams
### are no longer useful, as we have extracted all objects from
@@ -2148,41 +2153,36 @@ class PDFSerializer(object):
else:
data = obj.get_decdata()
self.serialize_object(obj.dic)
self.write('stream\n')
self.write(b'stream\n')
self.write(data)
self.write('\nendstream')
self.write(b'\nendstream')
else:
data = str(obj)
if data[0].isalnum() and self.last.isalnum():
self.write(' ')
data = str(obj).encode('utf-8')
if bytes([data[0]]).isalnum() and self.last.isalnum():
self.write(b' ')
self.write(data)
def serialize_indirect(self, objid, obj):
self.write('%d 0 obj' % (objid,))
self.write(b'%d 0 obj' % (objid,))
self.serialize_object(obj)
if self.last.isalnum():
self.write('\n')
self.write('endobj\n')
self.write(b'\n')
self.write(b'endobj\n')
def decryptBook(userkey, inpath, outpath):
if RSA is None:
raise ADEPTError("PyCrypto or OpenSSL must be installed.")
raise ADEPTError("PyCryptodome or OpenSSL must be installed.")
with open(inpath, 'rb') as inf:
#try:
serializer = PDFSerializer(inf, userkey)
#except:
# print "Error serializing pdf {0}. Probably wrong key.".format(os.path.basename(inpath))
# return 2
# hope this will fix the 'bad file descriptor' problem
with open(outpath, 'wb') as outf:
# help construct to make sure the method runs to the end
try:
serializer.dump(outf)
except Exception as e:
print("error writing pdf: {0}".format(e.args[0]))
print("error writing pdf: {0}".format(e))
return 2
return 0

View File

@@ -31,7 +31,7 @@ import struct
from io import BytesIO
from Crypto.Cipher import AES
from Crypto.Util.py3compat import bchr, bord
from Crypto.Util.py3compat import bchr
try:
# lzma library from calibre 4.6.0 or later
@@ -89,7 +89,7 @@ LEN_IS_VAR_LEN = 0xE
LEN_IS_NULL = 0xF
VERSION_MARKER = b"\x01\x00\xEA"
VERSION_MARKER = [b"\x01", b"\x00", b"\xEA"]
# asserts must always raise exceptions for proper functioning
@@ -347,7 +347,7 @@ class BinaryIonParser(object):
b = self.stream.read(1)
if len(b) < 1:
return -1
b = bord(b)
b = ord(b)
result = b >> 4
ln = b & 0xF
@@ -372,13 +372,13 @@ class BinaryIonParser(object):
return result
def readvarint(self):
b = bord(self.read())
b = ord(self.read())
negative = ((b & 0x40) != 0)
result = (b & 0x3F)
i = 0
while (b & 0x80) == 0 and i < 4:
b = bord(self.read())
b = ord(self.read())
result = (result << 7) | (b & 0x7F)
i += 1
@@ -389,12 +389,12 @@ class BinaryIonParser(object):
return result
def readvaruint(self):
b = bord(self.read())
b = ord(self.read())
result = (b & 0x7F)
i = 0
while (b & 0x80) == 0 and i < 4:
b = bord(self.read())
b = ord(self.read())
result = (result << 7) | (b & 0x7F)
i += 1
@@ -414,7 +414,7 @@ class BinaryIonParser(object):
_assert(self.localremaining <= 8, "Decimal overflow")
signed = False
b = [bord(x) for x in self.read(self.localremaining)]
b = [ord(x) for x in self.read(self.localremaining)]
if (b[0] & 0x80) != 0:
b[0] = b[0] & 0x7F
signed = True
@@ -579,7 +579,7 @@ class BinaryIonParser(object):
_assert(self.valuelen <= 4, "int too long: %d" % self.valuelen)
v = 0
for i in range(self.valuelen - 1, -1, -1):
v = (v | (bord(self.read()) << (i * 8)))
v = (v | (ord(self.read()) << (i * 8)))
if self.valuetid == TID_NEGINT:
self.value = -v
@@ -649,7 +649,7 @@ class BinaryIonParser(object):
result = ""
for i in b:
result += ("%02x " % bord(i))
result += ("%02x " % ord(i))
if len(result) > 0:
result = result[:-1]
@@ -748,7 +748,8 @@ def pkcs7pad(msg, blocklen):
def pkcs7unpad(msg, blocklen):
_assert(len(msg) % blocklen == 0)
paddinglen = bord(msg[-1])
paddinglen = msg[-1]
_assert(paddinglen > 0 and paddinglen <= blocklen, "Incorrect padding - Wrong key")
_assert(msg[-paddinglen:] == bchr(paddinglen) * paddinglen, "Incorrect padding - Wrong key")
@@ -806,7 +807,7 @@ class DrmIonVoucher(object):
secretkey = b""
def __init__(self, voucherenv, dsn, secret):
self.dsn,self.secret = dsn,secret
self.dsn, self.secret = dsn, secret
self.lockparams = []
@@ -827,7 +828,7 @@ class DrmIonVoucher(object):
sharedsecret = obfuscate(shared.encode('ASCII'), self.version)
key = hmac.new(sharedsecret, "PIDv3", digestmod=hashlib.sha256).digest()
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 = pkcs7unpad(b, 16)
@@ -1024,7 +1025,7 @@ class DrmIon(object):
outpages.write(msg)
return
_assert(msg[0] == b"\x00", "LZMA UseFilter not supported")
_assert(msg[0] == 0, "LZMA UseFilter not supported")
if calibre_lzma is not None:
with calibre_lzma.decompress(msg[1:], bufsize=0x1000000) as f:

View File

@@ -11,6 +11,10 @@ import shutil
import zipfile
from io import BytesIO
try:
from ion import DrmIon, DrmIonVoucher
except:
from calibre_plugins.dedrm.ion import DrmIon, DrmIonVoucher
__license__ = 'GPL v3'
@@ -27,22 +31,18 @@ class KFXZipBook:
return (None, None)
def processBook(self, totalpids):
try:
import ion
except:
from calibre_plugins.dedrm import ion
with zipfile.ZipFile(self.infile, 'r') as zf:
for filename in zf.namelist():
with zf.open(filename) as fh:
data = fh.read(8)
if data != '\xeaDRMION\xee':
if data != b'\xeaDRMION\xee':
continue
data += fh.read()
if self.voucher is None:
self.decrypt_voucher(totalpids)
print("Decrypting KFX DRMION: {0}".format(filename))
outfile = BytesIO()
ion.DrmIon(BytesIO(data[8:-8]), lambda name: self.voucher).parse(outfile)
DrmIon(BytesIO(data[8:-8]), lambda name: self.voucher).parse(outfile)
self.decrypted[filename] = outfile.getvalue()
if not self.decrypted:
@@ -53,11 +53,11 @@ class KFXZipBook:
for info in zf.infolist():
with zf.open(info.filename) as fh:
data = fh.read(4)
if data != '\xe0\x01\x00\xea':
if data != b'\xe0\x01\x00\xea':
continue
data += fh.read()
if 'ProtectedData' in data:
if b'ProtectedData' in data:
break # found DRM voucher
else:
raise Exception("The .kfx-zip archive contains an encrypted DRMION file without a DRM voucher")
@@ -72,7 +72,7 @@ class KFXZipBook:
continue
try:
voucher = ion.DrmIonVoucher(BytesIO(data), pid[:dsn_len], pid[dsn_len:])
voucher = DrmIonVoucher(BytesIO(data), pid[:dsn_len], pid[dsn_len:])
voucher.parse()
voucher.decryptvoucher()
break

View File

@@ -296,14 +296,14 @@ def getPidList(md1, md2, serials=[], kDatabases=[]):
for kDatabase in kDatabases:
try:
pidlst.extend(getK4Pids(md1, md2, kDatabase))
pidlst.extend(map(bytes,getK4Pids(md1, md2, kDatabase)))
except Exception as e:
print("Error getting PIDs from database {0}: {1}".format(kDatabase[0],e.args[0]))
traceback.print_exc()
for serialnum in serials:
try:
pidlst.extend(getKindlePids(md1, md2, serialnum))
pidlst.extend(map(bytes,getKindlePids(md1, md2, serialnum)))
except Exception as e:
print("Error getting PIDs from serial number {0}: {1}".format(serialnum ,e.args[0]))
traceback.print_exc()

View File

@@ -28,6 +28,7 @@ __version__ = '3.0'
# 2.5 - Final Fix for Windows user names with non-ascii characters, thanks to oneofusoneofus
# 2.6 - Start adding support for Kindle 1.25+ .kinf2018 file
# 2.7 - Finish .kinf2018 support, PC & Mac by Apprentice Sakuya
# 2.8 - Fix for Mac OS X Big Sur
# 3.0 - Python 3 for calibre 5.0
@@ -1174,8 +1175,11 @@ elif isosx:
libcrypto = find_library('crypto')
if libcrypto is None:
raise DrmException("libcrypto not found")
libcrypto = CDLL(libcrypto)
libcrypto = '/usr/lib/libcrypto.dylib'
try:
libcrypto = CDLL(libcrypto)
except Exception as e:
raise DrmException("libcrypto not found: " % e)
# From OpenSSL's crypto aes header
#

36
DeDRM_plugin/mobidedrm.py Normal file → Executable file
View File

@@ -191,19 +191,21 @@ def PC1(key, src, decryption=True):
dst+=bytes([curByte])
return dst
# accepts unicode returns unicode
def checksumPid(s):
letters = 'ABCDEFGHIJKLMNPQRSTUVWXYZ123456789'
crc = (~binascii.crc32(s,-1))&0xFFFFFFFF
crc = (~binascii.crc32(s.encode('utf-8'),-1))&0xFFFFFFFF
crc = crc ^ (crc >> 16)
res = s
l = len(letters)
for i in (0,1):
b = crc & 0xff
pos = (b // l) ^ (b % l)
res += letters[pos%l].encode('ascii')
res += letters[pos%l]
crc >>= 8
return res
# expects bytearray
def getSizeOfTrailingDataEntries(ptr, size, flags):
def getSizeOfTrailingDataEntry(ptr, size):
bitpos, result = 0, 0
@@ -282,7 +284,7 @@ class MobiBook:
self.mobi_codepage = 1252
self.mobi_version = -1
if self.magic == 'TEXtREAd':
if self.magic == b'TEXtREAd':
print("PalmDoc format book detected.")
return
@@ -301,7 +303,7 @@ class MobiBook:
# if exth region exists parse it for metadata array
try:
exth_flag, = struct.unpack('>L', self.sect[0x80:0x84])
exth = ''
exth = b''
if exth_flag & 0x40:
exth = self.sect[16 + self.mobi_length:]
if (len(exth) >= 12) and (exth[:4] == b'EXTH'):
@@ -323,12 +325,13 @@ class MobiBook:
except Exception as e:
print("Cannot set meta_array: Error: {:s}".format(e.args[0]))
#returns unicode
def getBookTitle(self):
codec_map = {
1252 : 'windows-1252',
65001 : 'utf-8',
}
title = ''
title = b''
codec = 'windows-1252'
if self.magic == b'BOOKMOBI':
if 503 in self.meta_array:
@@ -339,7 +342,7 @@ class MobiBook:
title = self.sect[toff:tend]
if self.mobi_codepage in codec_map.keys():
codec = codec_map[self.mobi_codepage]
if title == '':
if title == b'':
title = self.header[:32]
title = title.split(b'\0')[0]
return title.decode(codec)
@@ -355,13 +358,15 @@ class MobiBook:
# if that key exists in the meta_array, append its contents to the token
for i in range(0,len(data),5):
val, = struct.unpack('>I',data[i+1:i+5])
sval = self.meta_array.get(val,'')
sval = self.meta_array.get(val,b'')
token += sval
return rec209, token
# new must be byte array
def patch(self, off, new):
self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
# new must be byte array
def patchSection(self, section, new, in_off = 0):
if (section + 1 == self.num_sections):
endoff = len(self.data_file)
@@ -371,12 +376,12 @@ class MobiBook:
assert off + in_off + len(new) <= endoff
self.patch(off + in_off, new)
# pids in pidlist must be unicode, returned key is byte array, pid is unicode
def parseDRM(self, data, count, pidlist):
found_key = None
keyvec1 = b'\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96'
for pid in pidlist:
bigpid = pid.ljust(16,b'\0')
bigpid = bigpid
bigpid = pid.encode('utf-8').ljust(16,b'\0')
temp_key = PC1(keyvec1, bigpid, False)
temp_key_sum = sum(temp_key) & 0xff
found_key = None
@@ -424,6 +429,7 @@ class MobiBook:
return ".azw3"
return ".mobi"
# pids in pidlist may be unicode or bytearrays or bytes
def processBook(self, pidlist):
crypto_type, = struct.unpack('>H', self.sect[0xC:0xC+2])
print("Crypto Type is: {0:d}".format(crypto_type))
@@ -445,6 +451,8 @@ class MobiBook:
goodpids = []
# print("DEBUG ==== pidlist = ", pidlist)
for pid in pidlist:
if isinstance(pid,(bytearray,bytes)):
pid = pid.decode('utf-8')
if len(pid)==10:
if checksumPid(pid[0:-2]) != pid:
print("Warning: PID {0} has incorrect checksum, should have been {1}".format(pid,checksumPid(pid[0:-2])))
@@ -457,8 +465,8 @@ class MobiBook:
# print("======= DEBUG good pids = ", goodpids)
if self.crypto_type == 1:
t1_keyvec = 'QDCVEPMU675RUBSZ'
if self.magic == 'TEXtREAd':
t1_keyvec = b'QDCVEPMU675RUBSZ'
if self.magic == b'TEXtREAd':
bookkey_data = self.sect[0x0E:0x0E+16]
elif self.mobi_version < 0:
bookkey_data = self.sect[0x90:0x90+16]
@@ -473,7 +481,7 @@ class MobiBook:
raise DrmException("Encryption not initialised. Must be opened with Mobipocket Reader first.")
found_key, pid = self.parseDRM(self.sect[drm_ptr:drm_ptr+drm_size], drm_count, goodpids)
if not found_key:
raise DrmException("No key found in {0:d} keys tried.".format(len(goodpids)))
raise DrmException("No key found in {0:d} PIDs tried.".format(len(goodpids)))
# kill the drm keys
self.patchSection(0, b'\0' * drm_size, drm_ptr)
# kill the drm pointers
@@ -509,6 +517,7 @@ class MobiBook:
print("done")
return
# pids in pidlist must be unicode
def getUnencryptedBook(infile,pidlist):
if not os.path.isfile(infile):
raise DrmException("Input File Not Found.")
@@ -530,8 +539,7 @@ def cli_main():
infile = argv[1]
outfile = argv[2]
if len(argv) == 4:
# convert from unicode to bytearray before splitting.
pidlist = argv[3].encode('utf-8').split(b',')
pidlist = argv[3].split(',')
else:
pidlist = []
try:

View File

@@ -76,7 +76,7 @@ def load_libcrypto():
return ob.raw
def decrypt(self, data):
if not data:
return ''
return b''
i = 0
result = []
while i < len(data):
@@ -84,6 +84,6 @@ def load_libcrypto():
processed_block = self.desdecrypt(block)
result.append(processed_block)
i += 8
return ''.join(result)
return b''.join(result)
return DES

View File

@@ -66,21 +66,21 @@ class fixZip:
def uncompress(self, cmpdata):
dc = zlib.decompressobj(-15)
data = ''
data = b''
while len(cmpdata) > 0:
if len(cmpdata) > _MAX_SIZE :
newdata = cmpdata[0:_MAX_SIZE]
cmpdata = cmpdata[_MAX_SIZE:]
else:
newdata = cmpdata
cmpdata = ''
cmpdata = b''
newdata = dc.decompress(newdata)
unprocessed = dc.unconsumed_tail
if len(unprocessed) == 0:
newdata += dc.flush()
data += newdata
cmpdata += unprocessed
unprocessed = ''
unprocessed = b''
return data
def getfiledata(self, zi):
@@ -123,7 +123,7 @@ class fixZip:
mimeinfo.internal_attr = 1 # text file
try:
# if the mimetype is present, get its info, including time-stamp
oldmimeinfo = self.inzip.getinfo('mimetype')
oldmimeinfo = self.inzip.getinfo(b'mimetype')
# copy across useful fields
mimeinfo.date_time = oldmimeinfo.date_time
mimeinfo.comment = oldmimeinfo.comment
@@ -137,7 +137,7 @@ class fixZip:
# write the rest of the files
for zinfo in self.inzip.infolist():
if zinfo.filename != "mimetype" or self.ztype != 'epub':
if zinfo.filename != b"mimetype" or self.ztype != 'epub':
data = None
try:
data = self.inzip.read(zinfo.filename)

View File

@@ -18,7 +18,7 @@ Just download and use these tools, that's all! Uh, almost. There are a few, uh,
But otherwise, if your ebook is from Amazon, Kobo, Barnes & Noble or any of the ebook stores selling ebooks compatible with Adobe Digital Editions 2.0.1, you should be able to remove the DRM that's been applied to your ebooks.
### A Recent Change to Kindle for PC/Kindle for Mac
### Recent Changes to Kindle for PC/Kindle for Mac
Starting with version 1.19, Kindle for PC/Mac uses Amazon's new KFX format which isn't quite as good a source for conversion to ePub as the older KF8 (& MOBI) formats. There are two options to get the older formats. Either stick with version 1.17 or earlier, or modify the executable by changing a file name (PC) or disabling a component of the application (Mac).
Version 1.17 of Kindle is no longer available directly from Amazon, so you will need to search for the proper file name and find it on a third party site. The name is `KindleForPC-installer-1.17.44170.exe` for PC and `KindleForMac-44182.dmg` for Mac.
@@ -34,7 +34,7 @@ Verify the one of the following cryptographic hash values, using software of you
* SHA-1: 7AB9A86B954CB23D622BD79E3257F8E2182D791C
* SHA-256: 28DC21246A9C7CDEDD2D6F0F4082E6BF7EF9DB9CE9D485548E 8A9E1D19EAE2AC.
You will need to go to the preferences and uncheck the auto update checkbox. Then download and install 1.17 over the top of the newer installation. You'll also need to delete the KFX folders from your My Kindle Content folder.
You will need to go to the preferences and uncheck the auto update checkbox. Then download and install 1.17 over the top of the newer installation. You'll also need to delete the KFX folders from your My Kindle Content folder. You may also need to take further action to prevent an auto update. The simplest wayis to find the 'updates' folder and replace it with a file. See [this thread] (http://www.mobileread.com/forums/showthread.php?t=283371) at MobileRead for a Script to do this on a PC. On a Mac you can find the folder at ~/Library/Application Support/Kindle/ just delete the folder 'updates' and save a blank text file called 'updates' in its place.
Another possible solution is to use 1.19 or later, but disable KFX by renaming or disabling a necessary component of the application. This may or may not work on versions after 1.25. In a command window, enter the following commands when Kindle for PC/Mac is not running:
@@ -51,7 +51,7 @@ Mac Note: If the chmod command fails with a permission error try again using `su
After restarting the Kindle program any books previously downloaded in KFX format will no longer open. You will need to remove them from your device and re-download them. All future downloads will use the older Kindle formats instead of KFX although they will continue to be placed in one individual subdirectory per book. Note that books soudl be downoad by right-click and 'Download', not by just opening the book. Recent (1.25+) versions of Kindle for Mac/PC may convert KF8 files to a new format that is not supported by these tools when the book is opened for reading.
#### Decrypting KFX
Thanks to work by several people, the tools can now decrypt KFX format ebooks from Kindle for Mac/PC. In addition to the DeDRM plugin, calibre users will also need to install jhowell's KFX Input plugin which is available through the standard plugin menu in calibre, or directly from [his plugin thread](https://www.mobileread.com/forums/showthread.php?t=291290) on Mobileread.
Thanks to work by several people, the tools can now decrypt KFX format ebooks from Kindle for Mac/PC version 1.26 or earlier (version later than 1.26 use a new encryption scheme for KFX files). In addition to the DeDRM plugin, calibre users will also need to install jhowell's KFX Input plugin which is available through the standard plugin menu in calibre, or directly from [his plugin thread](https://www.mobileread.com/forums/showthread.php?t=291290) on Mobileread.
#### Thanks
Thanks to jhowell for his investigations into KFX format and the KFX Input plugin. Some of these instructions are from [his thread on the subject](https://www.mobileread.com/forums/showthread.php?t=283371) at MobileRead.
@@ -68,7 +68,7 @@ Install calibre. Install the DeDRM\_plugin in calibre. Install the Obok\_plugin
# Installing the Tools
## The calibre plugin
### I am trying to install the calibre plugin, but calibre says "ERROR: Unhandled exception: InvalidPlugin: The plugin in u[path]DeDRM\_tools\_6.5.3.zip is invalid. It does not contain a top-level \_\_init\_\_.py file"
You are trying to add the tools archive (e.g. `DeDRM_tools_6.5.3.zip`) instead of the plugin. The tools archive is not the plugin. It is a collection of DRM removal tools which includes the plugin. You must unzip the archive, and install the calibre plugin `DeDRM_plugin.zip` from a folder called `DeDRM_calibre_plugin` in the unzipped archive.
You are trying to add the tools archive (e.g. `DeDRM_tools_6.8.0.zip`) instead of the plugin. The tools archive is not the plugin. It is a collection of DRM removal tools which includes the plugin. You must unzip the archive, and install the calibre plugin `DeDRM_plugin.zip` from a folder called `DeDRM_calibre_plugin` in the unzipped archive.
### Ive unzipped the tools archive, but I cant find the calibre plugin when I try to add them to calibre. I use Windows.
You should select the zip file that is in the `DeDRM_calibre_plugin` folder, not any files inside the plugins zip archive. Make sure you are selecting from the folder that you created when you unzipped the tools archive and not selecting a file inside the still-zipped tools archive.

View File

@@ -197,7 +197,7 @@ class InterfacePluginAction(InterfaceAction):
# We will write the help file out every time, in case the user upgrades the plugin zip
# and there is a newer help file contained within it.
file_path = os.path.join(config_dir, 'plugins', HELPFILE_NAME)
file_data = self.load_resources(HELPFILE_NAME)[HELPFILE_NAME]
file_data = self.load_resources(HELPFILE_NAME)[HELPFILE_NAME].decode('utf-8')
with open(file_path,'w') as f:
f.write(file_data)
return file_path

Binary file not shown.

View File

@@ -8,7 +8,7 @@ msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2014-11-17 12:51+0100\n"
"PO-Revision-Date: 2020-02-02 09:18+0100\n"
"PO-Revision-Date: 2020-12-25 13:38+0100\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -16,14 +16,14 @@ msgstr ""
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 2.2.4\n"
"X-Generator: Poedit 2.4.2\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:80
msgid ""
"<p>No books found in Kobo Library\n"
"Are you sure it's installed\\configured\\synchronized?"
msgstr ""
"<p>Inga böcker finns i Kobo-bibliotek\n"
"<p>Inga böcker hittades i Kobo-bibliotek\n"
"Är du säker på att den är installerad\\konfigurerad\\synkroniserad?"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:87
@@ -36,17 +36,17 @@ msgstr "Problem med att hämta nycklar med nyare obok-metod."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:97
msgid "Found {0} possible keys to try."
msgstr "Hittade {0} möjliga nycklar att pröva med."
msgstr "Hittade {0} möjliga nycklar att försöka med."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:99
msgid "<p>No userkeys found to decrypt books with. No point in proceeding."
msgstr ""
"<p>Inga användarnycklar hittades för att dekryptera böcker med. Det är ingen "
"idé att fortsätta."
"<p>Inga användarnycklar hittades för att dekryptera böcker med. Ingen idé "
"att fortsätta."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:115
msgid "{} - Decryption canceled by user."
msgstr "{} - Dekryptering avbryts av användaren."
msgstr "{} - Dekryptering avbröts av användaren."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:135
msgid "{} - \"Add books\" canceled by user."
@@ -87,14 +87,14 @@ msgstr "dubblett upptäcktes"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:233
msgid "{0} - Successfully added EPUB format to existing {1}"
msgstr "{0} - Lade till EPUB-format till befintliga {1}"
msgstr "{0} - EPUB-format har lagts till i befintliga {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:236
msgid ""
"{0} - Error adding EPUB format to existing {1}. This really shouldn't happen."
msgstr ""
"{0} - Fel vid tillägg av EPUB-format till befintligt {1}. Det här borde inte "
"hända."
"{0} - Fel vid tilläggning av EPUB-format till befintligt {1}. Detta borde "
"verkligen inte hända."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:259
msgid "{} - \"Insert formats\" canceled by user."
@@ -103,15 +103,15 @@ msgstr "{} - \"Infoga format\" avbröts av användaren."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:291
msgid ""
"<p><b>{0}</b> EPUB{2} successfully added to library.<br /><br /><b>{1}</b> "
msgstr "<p><b>{0}</b> EPUB{2} lades till bibliotek.<br /><br /><b>{1}</b> "
msgstr "<p><b>{0}</b> EPUB{2} har lagts till i bibliotek.<br /><br /><b>{1}</b> "
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:292
msgid ""
"not added because books with the same title/author were detected.<br /><br /"
">Would you like to try and add the EPUB format{0}"
msgstr ""
"inte tillagd eftersom böcker med samma titel/författare upptäcktes.<br/><br /"
">Vill du försöka lägga till EPUB-formatet{0}"
"lades inte till eftersom böcker med samma titel/författare upptäcktes.<br/"
"><br />Vill du försöka lägga till EPUB-formatet{0}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:293
msgid ""
@@ -126,45 +126,46 @@ msgid ""
"{0} -- not added because of {1} in your library.\n"
"\n"
msgstr ""
"{0} -- inte tillagd på grund av {1} i ditt bibliotek.\n"
"{0} -- lades inte till på grund av {1} i ditt bibliotek.\n"
"\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:297
msgid "<p><b>{0}</b> -- not added because of {1} in your library.<br /><br />"
msgstr ""
"<p><b>{0}</b> -- inte tillagd på grund av {1} i ditt bibliotek.<br /><br />"
"<p><b>{0}</b> -- lades inte till på grund av {1} i ditt bibliotek.<br /><br /"
">"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:298
msgid ""
"Would you like to try and add the EPUB format to an available calibre "
"duplicate?<br /><br />"
msgstr ""
"Vill du försöka lägga till EPUB-formatet till en tillgänglig calibre-"
"dubblett?<br /><br />"
"Vill du försöka lägga till EPUB-format till en tillgänglig calibre-dubblett?"
"<br /><br />"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:299
msgid "NOTE: no pre-existing EPUB will be overwritten."
msgstr "OBS: ingen befintlig EPUB kommer att skrivas över."
msgstr "OBS: inga befintliga EPUB:er kommer att skrivas över."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:346
msgid "Trying key: "
msgstr "Prövar nyckel: "
msgstr "Försöker med nyckel: "
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:378
msgid "Decryption failed, trying next key."
msgstr "Det gick inte att dekryptera, prövar nästa nyckel."
msgstr "Det gick inte att dekryptera, försöker med nästa nyckel."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:382
msgid "Unknown Error decrypting, trying next key.."
msgstr "Okänt fel dekryptering, prövar nästa nyckel.."
msgstr "Okänt fel vid dekryptering, försöker med nästa nyckel.."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:395
msgid ""
"<p>All selected Kobo books added as new calibre books or inserted into "
"existing calibre ebooks.<br /><br />No issues."
msgstr ""
"<p>Alla valda Kobo-böcker läggs till som nya calibre-böcker eller infogas i "
"befintliga calibre-e-böcker.<br /><br />Inga problem."
"<p>Alla valda Kobo-böcker har lagts till som nya calibre-böcker eller "
"infogats i befintliga calibre-e-böcker.<br /><br />Inga problem."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:399
msgid "<p>{0} successfully added."
@@ -192,7 +193,7 @@ msgstr "<p><b>Nya böcker skapade:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:418
msgid "<p><b>Duplicates that weren't added:</b> {}</p>\n"
msgstr "<p><b>Dubbletter som inte tillsattes:</b> {}</p>\n"
msgstr "<p><b>Dubbletter som inte har lagts till:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:426
msgid "<p><b>Book imports cancelled by user:</b> {}</p>\n"
@@ -221,7 +222,7 @@ msgstr ""
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:444
msgid "<p><b>Format imports cancelled by user:</b> {}</p>\n"
msgstr "<p><b>Format-import avbröts av användaren:</b> {}</p>\n"
msgstr "<p><b>Formatimporten avbröts av användaren:</b> {}</p>\n"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:458
msgid "Unknown Book Title"
@@ -237,7 +238,10 @@ msgid ""
"entries HAD an EPUB format already."
msgstr ""
"användaren VALDE att inte infoga det nya EPUB-formatet, eller alla "
"befintliga calibre-poster hade redan ett EPUB-format."
"befintliga calibre-poster hade redan ett EPUB-format.\n"
"\n"
"användaren valde att inte infoga det nya EPUB-formatet, eller alla "
"befintliga kaliberposter hade redan ett EPUB-format."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:464
msgid "of unknown reasons. Gosh I'm embarrassed!"
@@ -245,7 +249,7 @@ msgstr "av okända skäl. Jag skäms!"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\action.py:465
msgid "<p>{0} not added because {1}"
msgstr "<p>{0} inte tillagd eftersom {1}"
msgstr "<p>{0} lades inte till på grund av {1}"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:226
msgid "Help"
@@ -254,31 +258,31 @@ msgstr "Hjälp"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:235
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\utilities.py:214
msgid "Restart required"
msgstr "Omstart krävs"
msgstr "Kräver omstart"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:236
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\utilities.py:215
msgid ""
"Title image not found - you must restart Calibre before using this plugin!"
msgstr ""
"Titelbild hittades inte - du måste starta calibre innan du använder denna "
"Titelbild hittades inte - du måste starta om calibre innan du använder denna "
"insticksmodul!"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\common_utils.py:322
msgid "Undefined"
msgstr "Obestämd"
msgstr "Odefinierad"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:30
msgid "When should Obok try to insert EPUBs into existing calibre entries?"
msgstr "När ska Obok försöka infoga EPUB:er i befintliga calibre-böcker?"
msgstr "När ska Obok försöka infoga EPUB:er i befintliga calibre-poster?"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:33
msgid ""
"<p>Default behavior when duplicates are detected. None of the choices will "
"cause calibre ebooks to be overwritten"
msgstr ""
"<p>Standardbeteende när dubbletter upptäcks. Inget av alternativen kommer "
"att orsaka calibre-e-böcker att skrivas över"
"<p>Standardbeteende när dubbletter upptäcks. Inget av alternativen kommer att "
"göra att calibre-e-böcker skrivs över"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\config.py:35
msgid "Ask"
@@ -323,7 +327,7 @@ msgstr "Välj alla böcker med DRM."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:95
msgid "All DRM free"
msgstr "Alla DRM fria"
msgstr "Alla utan DRM"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\dialogs.py:96
msgid "Select all books without DRM."
@@ -355,12 +359,12 @@ msgstr "Tar bort DRM från Kobo-kepubs och lägger till dem i biblioteket."
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:162
msgid "AES improper key used"
msgstr "AES felaktig nyckel används"
msgstr "Felaktig AES-nyckel används"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:167
msgid "Failed to initialize AES key"
msgstr "Det gick inte att initiera AES-nyckel"
msgstr "Misslyckades att initiera AES-nyckel"
#: I:\Herramientas\PoeditPortable\App\Poedit\bin\obok_plugin-3.1.0_trad\obok\obok.py:175
msgid "AES decryption failed"
msgstr "AES dekryptering misslyckades"
msgstr "AES-dekryptering misslyckades"

View File

@@ -1,24 +1,27 @@
# DeDRM_tools
DeDRM tools for ebooks
# [Guide] How to remove DRM
Refer to [Wiki Page](https://github.com/apprenticeharper/DeDRM_tools/wiki/Exactly-how-to-remove-DRM)
# calibre 5.0
Yes, we know that the plugin doesn't work with calibre 5.0. It will eventually, but there's a lot of code to be translated that does complicated stuff with bits and bytes that must be converted by hand. It'll take a while.
# DeDRM_tools
DeDRM tools for ebooks
This is a repository that tracks all the scripts and other tools for removing DRM from ebooks that I could find, committed in date order as best as I could manage. (Except for the Requiem tools for Apple's iBooks, and Convert LIT for Microsoft's .lit ebooks.) This includes the tools from a time before Apprentice Alf had a blog, and continues through to when Apprentice Harper (with help) took over maintenance of the tools.
This is a repository of all the scripts and other tools for removing DRM from ebooks that I could find, committed in date order as best as I could manage. (Except for the Requiem tools for Apple's iBooks, and Convert LIT for Microsoft's .lit ebooks.)
The individual scripts are now released as two plugins for calibre: DeDRM and Obok.
The DeDRM plugin handles books that use Amazon DRM, Adobe Digital Editions DRM (version 1), Barnes & Noble DRM, and some historical formats.
The Obok plugin handles Kobo DRM.
Mostly it tracks the tools released by Apprentice Alf, athough it also includes the individual tools and their histories from before Alf had a blog.
Users with calibre 5.x or later should use release 7.0.0b2 or later of the tools.
Users with calibe 4.x or earlier should use release 6.8.0 of the tools.
Users should download the latest zip archive.
Developers might be interested in forking the repository, as it contains unzipped versions of those tools that are zipped to make the changes over time easier to follow.
For the latest Amazon KFX format, users of the calibre plugin should also install the KFX Input plugin from the standard calibre plugin menu. It's also available from the MobileRead thread here: https://www.mobileread.com/forums/showthread.php?t=291290
Note that DRM can only be removed from KFX format files downloaded with Kindle for PC/Mac 1.26 or earlier. Amazon changes the DRM for KFX files in Kindle for PC/Mac 1.27 and later.
I welcome contributions from others to improve these tools, from expanding the range of books handled, improving key retrieval, to just general bug fixes, speed improvements and UI enhancements.
I urge people to read the FAQs. But to cover the most common: Use ADE 2.0.1 to be sure not to get the new DRM scheme that these tools can't handle. Do remember to unzip the downloaded archive to get the plugin. You can't load the whole archive into calibre.
I urge people to read the FAQs. But to cover the most common: Use ADE 2.0.1 to be sure not to get the new DRM scheme that these tools can't handle. Do remember to unzip the downloaded archive to get the plugin (beta versions may be just the plugin don't unzip that). You can't load the whole tools archive into calibre.
My special thanks to all those developers who have done the hard work of reverse engineering to provide the initial tools.

View File

@@ -26,6 +26,8 @@ The tools are provided in the form of plugins for calibre. Calibre is an open so
DeDRM plugin for calibre (Mac OS X, 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.0 of the plugins.
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.
Once installed and configured, you can simply add a DRM book to calibre and a DRM-free version will be imported into the calibre database. Note that DRM removal only occurs on IMPORT not on CONVERSION or at any other time. If you have already imported DRMed books you'll need to remove the books from calibre and re-import them.