tools v1.6
This commit is contained in:
@@ -29,8 +29,16 @@
|
||||
# 0.13 - Formatting fixes: retabbed file, removed trailing whitespace
|
||||
# and extra blank lines, converted CR/LF pairs at ends of each line,
|
||||
# and other cosmetic fixes.
|
||||
# 0.14 - Working out when the extra data flags are present has been problematic
|
||||
# Versions 7 through 9 have tried to tweak the conditions, but have been
|
||||
# only partially successful. Closer examination of lots of sample
|
||||
# files reveals that a confusin has arisen because trailing data entries
|
||||
# are not encrypted, but it turns out that the multibyte entries
|
||||
# in utf8 file are encrypted. (Although neither kind gets compressed.)
|
||||
# This knowledge leads to a simplification of the test for the
|
||||
# trailing data byte flags - version 5 and higher AND header size >= 0xE4.
|
||||
|
||||
__version__ = '0.13'
|
||||
__version__ = '0.14'
|
||||
|
||||
import sys
|
||||
import struct
|
||||
@@ -113,8 +121,10 @@ def getSizeOfTrailingDataEntries(ptr, size, flags):
|
||||
if testflags & 1:
|
||||
num += getSizeOfTrailingDataEntry(ptr, size - num)
|
||||
testflags >>= 1
|
||||
if flags & 1:
|
||||
num += (ord(ptr[size - num - 1]) & 0x3) + 1
|
||||
# Multibyte data, if present, is included in the encryption, so
|
||||
# we do not need to check the low bit.
|
||||
# if flags & 1:
|
||||
# num += (ord(ptr[size - num - 1]) & 0x3) + 1
|
||||
return num
|
||||
|
||||
class DrmStripper:
|
||||
@@ -188,7 +198,7 @@ class DrmStripper:
|
||||
extra_data_flags = 0
|
||||
print "MOBI header length = %d" %mobi_length
|
||||
print "MOBI header version = %d" %mobi_version
|
||||
if (mobi_length >= 0xE4) and (mobi_version > 5):
|
||||
if (mobi_length >= 0xE4) and (mobi_version >= 5):
|
||||
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
|
||||
print "Extra Data Flags = %d" %extra_data_flags
|
||||
|
||||
@@ -236,7 +246,7 @@ if not __name__ == "__main__":
|
||||
description = 'Removes DRM from secure Mobi files'
|
||||
supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
|
||||
author = 'The Dark Reverser' # The author of this plugin
|
||||
version = (0, 1, 2) # The version number of this plugin
|
||||
version = (0, 1, 4) # The version number of this plugin
|
||||
file_types = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
|
||||
on_import = True # Run this plugin during the import
|
||||
|
||||
|
||||
23
Kindle_Mobi_Tools/skindle-06/Makefile
Normal file
23
Kindle_Mobi_Tools/skindle-06/Makefile
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
OBJS=skindle.o md5.o sha1.o b64.o skinutils.o cbuf.o mobi.o tpz.o
|
||||
|
||||
CC=gcc
|
||||
LD=gcc
|
||||
EXE=skindle
|
||||
EXTRALIBS=libz.a -lCrypt32 -lAdvapi32
|
||||
CFLAGS=-mno-cygwin
|
||||
|
||||
#use the following to strip your binary
|
||||
LDFLAGS=-s -mno-cygwin
|
||||
#LDFLAGS=-mno-cygwin
|
||||
|
||||
all: $(EXE)
|
||||
|
||||
%.o: %.c
|
||||
$(CC) -c $(CFLAGS) -g $(INC) $< -o $@
|
||||
|
||||
$(EXE): $(OBJS)
|
||||
$(LD) $(LDFLAGS) -o $@ -g $(OBJS) $(EXTRALIBS)
|
||||
|
||||
clean:
|
||||
-@rm *.o
|
||||
85
Kindle_Mobi_Tools/skindle-06/README.txt
Normal file
85
Kindle_Mobi_Tools/skindle-06/README.txt
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Dependencies: zlib (included)
|
||||
* build on cygwin using make and the included make file
|
||||
* A fully functionaly windows executable is included
|
||||
*/
|
||||
|
||||
/*
|
||||
* MUST be run on the computer on which KindleForPC is installed
|
||||
* under the account that was used to purchase DRM'ed content.
|
||||
* Requires your kindle.info file which can be found in something like:
|
||||
* <User home>\...\Amazon\Kindle For PC\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}
|
||||
* where ... varies by platform but is "Local Settings\Application Data" on XP
|
||||
* skindle will attempt to find this file automatically.
|
||||
*/
|
||||
|
||||
/*
|
||||
What: KindleForPC DRM removal utility to preserve your fair use rights!
|
||||
Why: Fair use is a well established doctrine, and I am no fan of vendor
|
||||
lockin.
|
||||
How: This utility implements the PID extraction, DRM key generation and
|
||||
decryption algorithms employed by the KindleForPC application. This
|
||||
is a stand alone app that does not require you to determine a PID on
|
||||
your own, and it does not need to run KindleForPC in order to extract
|
||||
any data from memory.
|
||||
|
||||
Shoutz: The DarkReverser - thanks for mobidedrm! The last part of this
|
||||
is just a C port of mobidedrm.
|
||||
labba and I<3cabbages for motivating me to do this the right way.
|
||||
You guys shouldn't need to spend all your time responding to all the
|
||||
changes Amazon is going to force you to make in unswindle each time
|
||||
the release a new version.
|
||||
CMBDTC - nice work on the topaz break!
|
||||
Lawrence Lessig - You are my hero. 'Nuff said.
|
||||
Cory Doctorow - A voice of reason in a sea of insanity
|
||||
Thumbs down: Disney, MPAA, RIAA - you guys suck. Making billions off
|
||||
of the exploitation of works out of copyright while vigourously
|
||||
pushing copyright extension to prevent others from doing the same
|
||||
is the height of hypocrasy.
|
||||
Congress - you guys suck too. Why you arrogant pricks think you
|
||||
are smarter than the founding fathers is beyond me.
|
||||
*/
|
||||
|
||||
Rationale:
|
||||
Need a tool to enable fair use of purchased ebook content.
|
||||
Need a tool that is not dependent on any particular version of
|
||||
KindleForPC and that does not need to run KindleForPC in order to
|
||||
extract a PID. The tool documents the structure of the kindle.info
|
||||
file and the data and algorthims that are used to derive per book
|
||||
PID values.
|
||||
|
||||
Installing:
|
||||
A compiled binary is included. Though it was built using cygwin, it
|
||||
should not require a cygwin installation in order to run it. To build
|
||||
from source, you will need cygwin with gcc and make.
|
||||
This has not been tested with Visual Studio, though you may be able to
|
||||
pile all the files into a project and add the Crypt32.lib, Advapi32 and
|
||||
zlib1 dependencies to build it.
|
||||
|
||||
usage: ./skindle [-d] [-v] -i <ebook file> -o <output file> [-k kindle.info file] [-p pid]
|
||||
-d optional, for topaz files only, produce a decompressed output file
|
||||
-i required name of the input mobi or topaz file
|
||||
-o required name of the output file to generate
|
||||
-k optional kindle.info path
|
||||
-v dump the contents of kindle.info
|
||||
-p additional PID values to attempt (can specifiy multiple times)
|
||||
|
||||
You only need to specify a kindle.info path if skindle can't find
|
||||
your kindle.info file automatically
|
||||
80
Kindle_Mobi_Tools/skindle-06/b64.c
Normal file
80
Kindle_Mobi_Tools/skindle-06/b64.c
Normal file
@@ -0,0 +1,80 @@
|
||||
/*********************************************************************\
|
||||
LICENCE: Copyright (c) 2001 Bob Trower, Trantor Standard Systems Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute,
|
||||
sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall
|
||||
be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||||
KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||||
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
|
||||
OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
VERSION HISTORY:
|
||||
Bob Trower 08/04/01 -- Create Version 0.00.00B
|
||||
|
||||
\******************************************************************* */
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
** Translation Table as described in RFC1113
|
||||
*/
|
||||
static const char cb64[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/*
|
||||
** encodeblock
|
||||
**
|
||||
** encode 3 8-bit binary bytes as 4 '6-bit' characters
|
||||
*/
|
||||
void encodeblock(unsigned char in[3], unsigned char out[4], int len) {
|
||||
out[0] = cb64[ in[0] >> 2 ];
|
||||
out[1] = cb64[ ((in[0] & 0x03) << 4) | ((in[1] & 0xf0) >> 4) ];
|
||||
out[2] = (unsigned char) (len > 1 ? cb64[ ((in[1] & 0x0f) << 2) | ((in[2] & 0xc0) >> 6) ] : '=');
|
||||
out[3] = (unsigned char) (len > 2 ? cb64[ in[2] & 0x3f ] : '=');
|
||||
}
|
||||
|
||||
/*
|
||||
** encode
|
||||
**
|
||||
** base64 encode a stream adding padding and line breaks as per spec.
|
||||
*/
|
||||
unsigned int base64(unsigned char *inbuf, unsigned int len, unsigned char *outbuf) {
|
||||
unsigned char in[3], out[4];
|
||||
int c;
|
||||
unsigned int i = 0;
|
||||
unsigned int outlen = 0;
|
||||
while (i < len) {
|
||||
int n = 0;
|
||||
for(c = 0; c < 3; c++, i++) {
|
||||
if (i < len) {
|
||||
in[c] = inbuf[i];
|
||||
n++;
|
||||
}
|
||||
else {
|
||||
in[c] = 0;
|
||||
}
|
||||
}
|
||||
if (n) {
|
||||
encodeblock(in, out, n);
|
||||
for(c = 0; c < 4; c++) {
|
||||
outbuf[outlen++] = out[c];
|
||||
}
|
||||
}
|
||||
}
|
||||
return outlen;
|
||||
}
|
||||
|
||||
85
Kindle_Mobi_Tools/skindle-06/cbuf.c
Normal file
85
Kindle_Mobi_Tools/skindle-06/cbuf.c
Normal file
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "cbuf.h"
|
||||
|
||||
cbuf *b_new(unsigned int size) {
|
||||
cbuf *b = (cbuf*)calloc(sizeof(cbuf), 1);
|
||||
if (b) {
|
||||
b->buf = (unsigned char *)malloc(size);
|
||||
b->size = b->buf ? size : 0;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
void b_free(cbuf *b) {
|
||||
if (b) {
|
||||
free(b->buf);
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
|
||||
void b_add_byte(cbuf *b, unsigned char ch) {
|
||||
if (b == NULL) return;
|
||||
if (b->idx == b->size) {
|
||||
unsigned char *p = realloc(b->buf, b->size * 2);
|
||||
if (p) {
|
||||
b->buf = p;
|
||||
b->size = b->size * 2;
|
||||
}
|
||||
}
|
||||
if (b->idx < b->size) {
|
||||
b->buf[b->idx++] = ch;
|
||||
}
|
||||
}
|
||||
|
||||
void b_add_buf(cbuf *b, unsigned char *buf, unsigned int len) {
|
||||
if (b == NULL) return;
|
||||
unsigned int new_sz = b->idx + len;
|
||||
while (b->size < new_sz) {
|
||||
unsigned char *p = realloc(b->buf, b->size * 2);
|
||||
if (p) {
|
||||
b->buf = p;
|
||||
b->size = b->size * 2;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if ((b->idx + len) <= b->size) {
|
||||
memcpy(b->buf + b->idx, buf, len);
|
||||
b->idx += len;
|
||||
}
|
||||
}
|
||||
|
||||
void b_add_str(cbuf *b, const char *buf) {
|
||||
if (b == NULL) return;
|
||||
unsigned int len = strlen(buf);
|
||||
unsigned int new_sz = b->idx + len;
|
||||
while (b->size < new_sz) {
|
||||
unsigned char *p = realloc(b->buf, b->size * 2);
|
||||
if (p) {
|
||||
b->buf = p;
|
||||
b->size = b->size * 2;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
if ((b->idx + len) <= b->size) {
|
||||
memcpy(b->buf + b->idx, buf, len);
|
||||
b->idx += len;
|
||||
}
|
||||
}
|
||||
|
||||
32
Kindle_Mobi_Tools/skindle-06/cbuf.h
Normal file
32
Kindle_Mobi_Tools/skindle-06/cbuf.h
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __CBUF_H
|
||||
#define __CBUF_H
|
||||
|
||||
typedef struct _cbuf {
|
||||
unsigned int size; //current size
|
||||
unsigned int idx; //current position
|
||||
unsigned char *buf;
|
||||
} cbuf;
|
||||
|
||||
cbuf *b_new(unsigned int size);
|
||||
void b_free(cbuf *b);
|
||||
void b_add_byte(cbuf *b, unsigned char ch);
|
||||
void b_add_buf(cbuf *b, unsigned char *buf, unsigned int len);
|
||||
void b_add_str(cbuf *b, const char *buf);
|
||||
|
||||
#endif
|
||||
BIN
Kindle_Mobi_Tools/skindle-06/libz.a
Normal file
BIN
Kindle_Mobi_Tools/skindle-06/libz.a
Normal file
Binary file not shown.
381
Kindle_Mobi_Tools/skindle-06/md5.c
Normal file
381
Kindle_Mobi_Tools/skindle-06/md5.c
Normal file
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.c is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order
|
||||
either statically or dynamically; added missing #include <string.h>
|
||||
in library.
|
||||
2002-03-11 lpd Corrected argument list for main(), and added int return
|
||||
type, in test program and T value program.
|
||||
2002-02-21 lpd Added missing #include <stdio.h> in test program.
|
||||
2000-07-03 lpd Patched to eliminate warnings about "constant is
|
||||
unsigned in ANSI C, signed in traditional"; made test program
|
||||
self-checking.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5).
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#include "md5.h"
|
||||
#include <string.h>
|
||||
|
||||
#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */
|
||||
#ifdef ARCH_IS_BIG_ENDIAN
|
||||
# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1)
|
||||
#else
|
||||
# define BYTE_ORDER 0
|
||||
#endif
|
||||
|
||||
#define T_MASK ((md5_word_t)~0)
|
||||
#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87)
|
||||
#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9)
|
||||
#define T3 0x242070db
|
||||
#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111)
|
||||
#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050)
|
||||
#define T6 0x4787c62a
|
||||
#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec)
|
||||
#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe)
|
||||
#define T9 0x698098d8
|
||||
#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850)
|
||||
#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e)
|
||||
#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841)
|
||||
#define T13 0x6b901122
|
||||
#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c)
|
||||
#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71)
|
||||
#define T16 0x49b40821
|
||||
#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d)
|
||||
#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf)
|
||||
#define T19 0x265e5a51
|
||||
#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855)
|
||||
#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2)
|
||||
#define T22 0x02441453
|
||||
#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e)
|
||||
#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437)
|
||||
#define T25 0x21e1cde6
|
||||
#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829)
|
||||
#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278)
|
||||
#define T28 0x455a14ed
|
||||
#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa)
|
||||
#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07)
|
||||
#define T31 0x676f02d9
|
||||
#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375)
|
||||
#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd)
|
||||
#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e)
|
||||
#define T35 0x6d9d6122
|
||||
#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3)
|
||||
#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb)
|
||||
#define T38 0x4bdecfa9
|
||||
#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f)
|
||||
#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f)
|
||||
#define T41 0x289b7ec6
|
||||
#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805)
|
||||
#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a)
|
||||
#define T44 0x04881d05
|
||||
#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6)
|
||||
#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a)
|
||||
#define T47 0x1fa27cf8
|
||||
#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a)
|
||||
#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb)
|
||||
#define T50 0x432aff97
|
||||
#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58)
|
||||
#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6)
|
||||
#define T53 0x655b59c3
|
||||
#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d)
|
||||
#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82)
|
||||
#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e)
|
||||
#define T57 0x6fa87e4f
|
||||
#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f)
|
||||
#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb)
|
||||
#define T60 0x4e0811a1
|
||||
#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d)
|
||||
#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca)
|
||||
#define T63 0x2ad7d2bb
|
||||
#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e)
|
||||
|
||||
|
||||
static void
|
||||
md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/)
|
||||
{
|
||||
md5_word_t
|
||||
a = pms->abcd[0], b = pms->abcd[1],
|
||||
c = pms->abcd[2], d = pms->abcd[3];
|
||||
md5_word_t t;
|
||||
#if BYTE_ORDER > 0
|
||||
/* Define storage only for big-endian CPUs. */
|
||||
md5_word_t X[16];
|
||||
#else
|
||||
/* Define storage for little-endian or both types of CPUs. */
|
||||
md5_word_t xbuf[16];
|
||||
const md5_word_t *X;
|
||||
#endif
|
||||
|
||||
{
|
||||
#if BYTE_ORDER == 0
|
||||
/*
|
||||
* Determine dynamically whether this is a big-endian or
|
||||
* little-endian machine, since we can use a more efficient
|
||||
* algorithm on the latter.
|
||||
*/
|
||||
static const int w = 1;
|
||||
|
||||
if (*((const md5_byte_t *)&w)) /* dynamic little-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER <= 0 /* little-endian */
|
||||
{
|
||||
/*
|
||||
* On little-endian machines, we can process properly aligned
|
||||
* data without copying it.
|
||||
*/
|
||||
if (!((data - (const md5_byte_t *)0) & 3)) {
|
||||
/* data are properly aligned */
|
||||
X = (const md5_word_t *)data;
|
||||
} else {
|
||||
/* not aligned */
|
||||
memcpy(xbuf, data, 64);
|
||||
X = xbuf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if BYTE_ORDER == 0
|
||||
else /* dynamic big-endian */
|
||||
#endif
|
||||
#if BYTE_ORDER >= 0 /* big-endian */
|
||||
{
|
||||
/*
|
||||
* On big-endian machines, we must arrange the bytes in the
|
||||
* right order.
|
||||
*/
|
||||
const md5_byte_t *xp = data;
|
||||
int i;
|
||||
|
||||
# if BYTE_ORDER == 0
|
||||
X = xbuf; /* (dynamic only) */
|
||||
# else
|
||||
# define xbuf X /* (static only) */
|
||||
# endif
|
||||
for (i = 0; i < 16; ++i, xp += 4)
|
||||
xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
|
||||
|
||||
/* Round 1. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define F(x, y, z) (((x) & (y)) | (~(x) & (z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + F(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 7, T1);
|
||||
SET(d, a, b, c, 1, 12, T2);
|
||||
SET(c, d, a, b, 2, 17, T3);
|
||||
SET(b, c, d, a, 3, 22, T4);
|
||||
SET(a, b, c, d, 4, 7, T5);
|
||||
SET(d, a, b, c, 5, 12, T6);
|
||||
SET(c, d, a, b, 6, 17, T7);
|
||||
SET(b, c, d, a, 7, 22, T8);
|
||||
SET(a, b, c, d, 8, 7, T9);
|
||||
SET(d, a, b, c, 9, 12, T10);
|
||||
SET(c, d, a, b, 10, 17, T11);
|
||||
SET(b, c, d, a, 11, 22, T12);
|
||||
SET(a, b, c, d, 12, 7, T13);
|
||||
SET(d, a, b, c, 13, 12, T14);
|
||||
SET(c, d, a, b, 14, 17, T15);
|
||||
SET(b, c, d, a, 15, 22, T16);
|
||||
#undef SET
|
||||
|
||||
/* Round 2. */
|
||||
/* Let [abcd k s i] denote the operation
|
||||
a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define G(x, y, z) (((x) & (z)) | ((y) & ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + G(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 1, 5, T17);
|
||||
SET(d, a, b, c, 6, 9, T18);
|
||||
SET(c, d, a, b, 11, 14, T19);
|
||||
SET(b, c, d, a, 0, 20, T20);
|
||||
SET(a, b, c, d, 5, 5, T21);
|
||||
SET(d, a, b, c, 10, 9, T22);
|
||||
SET(c, d, a, b, 15, 14, T23);
|
||||
SET(b, c, d, a, 4, 20, T24);
|
||||
SET(a, b, c, d, 9, 5, T25);
|
||||
SET(d, a, b, c, 14, 9, T26);
|
||||
SET(c, d, a, b, 3, 14, T27);
|
||||
SET(b, c, d, a, 8, 20, T28);
|
||||
SET(a, b, c, d, 13, 5, T29);
|
||||
SET(d, a, b, c, 2, 9, T30);
|
||||
SET(c, d, a, b, 7, 14, T31);
|
||||
SET(b, c, d, a, 12, 20, T32);
|
||||
#undef SET
|
||||
|
||||
/* Round 3. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define H(x, y, z) ((x) ^ (y) ^ (z))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + H(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 5, 4, T33);
|
||||
SET(d, a, b, c, 8, 11, T34);
|
||||
SET(c, d, a, b, 11, 16, T35);
|
||||
SET(b, c, d, a, 14, 23, T36);
|
||||
SET(a, b, c, d, 1, 4, T37);
|
||||
SET(d, a, b, c, 4, 11, T38);
|
||||
SET(c, d, a, b, 7, 16, T39);
|
||||
SET(b, c, d, a, 10, 23, T40);
|
||||
SET(a, b, c, d, 13, 4, T41);
|
||||
SET(d, a, b, c, 0, 11, T42);
|
||||
SET(c, d, a, b, 3, 16, T43);
|
||||
SET(b, c, d, a, 6, 23, T44);
|
||||
SET(a, b, c, d, 9, 4, T45);
|
||||
SET(d, a, b, c, 12, 11, T46);
|
||||
SET(c, d, a, b, 15, 16, T47);
|
||||
SET(b, c, d, a, 2, 23, T48);
|
||||
#undef SET
|
||||
|
||||
/* Round 4. */
|
||||
/* Let [abcd k s t] denote the operation
|
||||
a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */
|
||||
#define I(x, y, z) ((y) ^ ((x) | ~(z)))
|
||||
#define SET(a, b, c, d, k, s, Ti)\
|
||||
t = a + I(b,c,d) + X[k] + Ti;\
|
||||
a = ROTATE_LEFT(t, s) + b
|
||||
/* Do the following 16 operations. */
|
||||
SET(a, b, c, d, 0, 6, T49);
|
||||
SET(d, a, b, c, 7, 10, T50);
|
||||
SET(c, d, a, b, 14, 15, T51);
|
||||
SET(b, c, d, a, 5, 21, T52);
|
||||
SET(a, b, c, d, 12, 6, T53);
|
||||
SET(d, a, b, c, 3, 10, T54);
|
||||
SET(c, d, a, b, 10, 15, T55);
|
||||
SET(b, c, d, a, 1, 21, T56);
|
||||
SET(a, b, c, d, 8, 6, T57);
|
||||
SET(d, a, b, c, 15, 10, T58);
|
||||
SET(c, d, a, b, 6, 15, T59);
|
||||
SET(b, c, d, a, 13, 21, T60);
|
||||
SET(a, b, c, d, 4, 6, T61);
|
||||
SET(d, a, b, c, 11, 10, T62);
|
||||
SET(c, d, a, b, 2, 15, T63);
|
||||
SET(b, c, d, a, 9, 21, T64);
|
||||
#undef SET
|
||||
|
||||
/* Then perform the following additions. (That is increment each
|
||||
of the four registers by the value it had before this block
|
||||
was started.) */
|
||||
pms->abcd[0] += a;
|
||||
pms->abcd[1] += b;
|
||||
pms->abcd[2] += c;
|
||||
pms->abcd[3] += d;
|
||||
}
|
||||
|
||||
void
|
||||
md5_init(md5_state_t *pms)
|
||||
{
|
||||
pms->count[0] = pms->count[1] = 0;
|
||||
pms->abcd[0] = 0x67452301;
|
||||
pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476;
|
||||
pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301;
|
||||
pms->abcd[3] = 0x10325476;
|
||||
}
|
||||
|
||||
void
|
||||
md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes)
|
||||
{
|
||||
const md5_byte_t *p = data;
|
||||
int left = nbytes;
|
||||
int offset = (pms->count[0] >> 3) & 63;
|
||||
md5_word_t nbits = (md5_word_t)(nbytes << 3);
|
||||
|
||||
if (nbytes <= 0)
|
||||
return;
|
||||
|
||||
/* Update the message length. */
|
||||
pms->count[1] += nbytes >> 29;
|
||||
pms->count[0] += nbits;
|
||||
if (pms->count[0] < nbits)
|
||||
pms->count[1]++;
|
||||
|
||||
/* Process an initial partial block. */
|
||||
if (offset) {
|
||||
int copy = (offset + nbytes > 64 ? 64 - offset : nbytes);
|
||||
|
||||
memcpy(pms->buf + offset, p, copy);
|
||||
if (offset + copy < 64)
|
||||
return;
|
||||
p += copy;
|
||||
left -= copy;
|
||||
md5_process(pms, pms->buf);
|
||||
}
|
||||
|
||||
/* Process full blocks. */
|
||||
for (; left >= 64; p += 64, left -= 64)
|
||||
md5_process(pms, p);
|
||||
|
||||
/* Process a final partial block. */
|
||||
if (left)
|
||||
memcpy(pms->buf, p, left);
|
||||
}
|
||||
|
||||
void
|
||||
md5_finish(md5_state_t *pms, md5_byte_t digest[16])
|
||||
{
|
||||
static const md5_byte_t pad[64] = {
|
||||
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
md5_byte_t data[8];
|
||||
int i;
|
||||
|
||||
/* Save the length before padding. */
|
||||
for (i = 0; i < 8; ++i)
|
||||
data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3));
|
||||
/* Pad to 56 bytes mod 64. */
|
||||
md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1);
|
||||
/* Append the length. */
|
||||
md5_append(pms, data, 8);
|
||||
for (i = 0; i < 16; ++i)
|
||||
digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3));
|
||||
}
|
||||
91
Kindle_Mobi_Tools/skindle-06/md5.h
Normal file
91
Kindle_Mobi_Tools/skindle-06/md5.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
L. Peter Deutsch
|
||||
ghost@aladdin.com
|
||||
|
||||
*/
|
||||
/* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */
|
||||
/*
|
||||
Independent implementation of MD5 (RFC 1321).
|
||||
|
||||
This code implements the MD5 Algorithm defined in RFC 1321, whose
|
||||
text is available at
|
||||
http://www.ietf.org/rfc/rfc1321.txt
|
||||
The code is derived from the text of the RFC, including the test suite
|
||||
(section A.5) but excluding the rest of Appendix A. It does not include
|
||||
any code or documentation that is identified in the RFC as being
|
||||
copyrighted.
|
||||
|
||||
The original and principal author of md5.h is L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Other authors are noted in the change history
|
||||
that follows (in reverse chronological order):
|
||||
|
||||
2002-04-13 lpd Removed support for non-ANSI compilers; removed
|
||||
references to Ghostscript; clarified derivation from RFC 1321;
|
||||
now handles byte order either statically or dynamically.
|
||||
1999-11-04 lpd Edited comments slightly for automatic TOC extraction.
|
||||
1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5);
|
||||
added conditionalization for C++ compilation from Martin
|
||||
Purschke <purschke@bnl.gov>.
|
||||
1999-05-03 lpd Original version.
|
||||
*/
|
||||
|
||||
#ifndef md5_INCLUDED
|
||||
# define md5_INCLUDED
|
||||
|
||||
/*
|
||||
* This package supports both compile-time and run-time determination of CPU
|
||||
* byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be
|
||||
* compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is
|
||||
* defined as non-zero, the code will be compiled to run only on big-endian
|
||||
* CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to
|
||||
* run on either big- or little-endian CPUs, but will run slightly less
|
||||
* efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined.
|
||||
*/
|
||||
|
||||
typedef unsigned char md5_byte_t; /* 8-bit byte */
|
||||
typedef unsigned int md5_word_t; /* 32-bit word */
|
||||
|
||||
/* Define the state of the MD5 Algorithm. */
|
||||
typedef struct md5_state_s {
|
||||
md5_word_t count[2]; /* message length in bits, lsw first */
|
||||
md5_word_t abcd[4]; /* digest buffer */
|
||||
md5_byte_t buf[64]; /* accumulate block */
|
||||
} md5_state_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
/* Initialize the algorithm. */
|
||||
void md5_init(md5_state_t *pms);
|
||||
|
||||
/* Append a string to the message. */
|
||||
void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes);
|
||||
|
||||
/* Finish the message and return the digest. */
|
||||
void md5_finish(md5_state_t *pms, md5_byte_t digest[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* md5_INCLUDED */
|
||||
365
Kindle_Mobi_Tools/skindle-06/mobi.c
Normal file
365
Kindle_Mobi_Tools/skindle-06/mobi.c
Normal file
@@ -0,0 +1,365 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "mobi.h"
|
||||
|
||||
unsigned char *getExthData(MobiFile *book, unsigned int type, unsigned int *len) {
|
||||
unsigned int i;
|
||||
unsigned int exthRecords = bswap_l(book->exth->recordCount);
|
||||
ExthRecHeader *erh = book->exth->records;
|
||||
|
||||
*len = 0;
|
||||
|
||||
for (i = 0; i < exthRecords; i++) {
|
||||
unsigned int recType = bswap_l(erh->type);
|
||||
unsigned int recLen = bswap_l(erh->len);
|
||||
|
||||
if (recLen < 8) {
|
||||
fprintf(stderr, "Invalid exth record length %d\n", i);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (recType == type) {
|
||||
*len = recLen - 8;
|
||||
return (unsigned char*)(erh + 1);
|
||||
}
|
||||
erh = (ExthRecHeader*)(recLen + (char*)erh);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void enumExthRecords(ExthHeader *eh) {
|
||||
unsigned int exthRecords = bswap_l(eh->recordCount);
|
||||
unsigned int i;
|
||||
unsigned char *data;
|
||||
ExthRecHeader *erh = eh->records;
|
||||
|
||||
for (i = 0; i < exthRecords; i++) {
|
||||
unsigned int recType = bswap_l(erh->type);
|
||||
unsigned int recLen = bswap_l(erh->len);
|
||||
|
||||
fprintf(stderr, "%d: type - %d, len %d\n", i, recType, recLen);
|
||||
|
||||
if (recLen < 8) {
|
||||
fprintf(stderr, "Invalid exth record length %d\n", i);
|
||||
return;
|
||||
}
|
||||
|
||||
data = (unsigned char*)(erh + 1);
|
||||
switch (recType) {
|
||||
case 1: //drm_server_id
|
||||
fprintf(stderr, "drm_server_id: %s\n", data);
|
||||
break;
|
||||
case 2: //drm_commerce_id
|
||||
fprintf(stderr, "drm_commerce_id: %s\n", data);
|
||||
break;
|
||||
case 3: //drm_ebookbase_book_id
|
||||
fprintf(stderr, "drm_ebookbase_book_id: %s\n", data);
|
||||
break;
|
||||
case 100: //author
|
||||
fprintf(stderr, "author: %s\n", data);
|
||||
break;
|
||||
case 101: //publisher
|
||||
fprintf(stderr, "publisher: %s\n", data);
|
||||
break;
|
||||
case 106: //publishingdate
|
||||
fprintf(stderr, "publishingdate: %s\n", data);
|
||||
break;
|
||||
case 113: //asin
|
||||
fprintf(stderr, "asin: %s\n", data);
|
||||
break;
|
||||
case 208: //book unique drm key
|
||||
fprintf(stderr, "book drm key: %s\n", data);
|
||||
break;
|
||||
case 503: //updatedtitle
|
||||
fprintf(stderr, "updatedtitle: %s\n", data);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
erh = (ExthRecHeader*)(recLen + (char*)erh);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//implementation of Pukall Cipher 1
|
||||
unsigned char *PC1(unsigned char *key, unsigned int klen, unsigned char *src,
|
||||
unsigned char *dest, unsigned int len, int decryption) {
|
||||
unsigned int sum1 = 0;
|
||||
unsigned int sum2 = 0;
|
||||
unsigned int keyXorVal = 0;
|
||||
unsigned short wkey[8];
|
||||
unsigned int i;
|
||||
if (klen != 16) {
|
||||
fprintf(stderr, "Bad key length!\n");
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < 8; i++) {
|
||||
wkey[i] = (key[i * 2] << 8) | key[i * 2 + 1];
|
||||
}
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned int temp1 = 0;
|
||||
unsigned int byteXorVal = 0;
|
||||
unsigned int j, curByte;
|
||||
for (j = 0; j < 8; j++) {
|
||||
temp1 ^= wkey[j];
|
||||
sum2 = (sum2 + j) * 20021 + sum1;
|
||||
sum1 = (temp1 * 346) & 0xFFFF;
|
||||
sum2 = (sum2 + sum1) & 0xFFFF;
|
||||
temp1 = (temp1 * 20021 + 1) & 0xFFFF;
|
||||
byteXorVal ^= temp1 ^ sum2;
|
||||
}
|
||||
curByte = src[i];
|
||||
if (!decryption) {
|
||||
keyXorVal = curByte * 257;
|
||||
}
|
||||
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF;
|
||||
if (decryption) {
|
||||
keyXorVal = curByte * 257;
|
||||
}
|
||||
for (j = 0; j < 8; j++) {
|
||||
wkey[j] ^= keyXorVal;
|
||||
}
|
||||
dest[i] = curByte;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
unsigned int getSizeOfTrailingDataEntry(unsigned char *ptr, unsigned int size) {
|
||||
unsigned int bitpos = 0;
|
||||
unsigned int result = 0;
|
||||
if (size <= 0) {
|
||||
return result;
|
||||
}
|
||||
while (1) {
|
||||
unsigned int v = ptr[size - 1];
|
||||
result |= (v & 0x7F) << bitpos;
|
||||
bitpos += 7;
|
||||
size -= 1;
|
||||
if ((v & 0x80) != 0 || (bitpos >= 28) || (size == 0)) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int getSizeOfTrailingDataEntries(unsigned char *ptr, unsigned int size, unsigned int flags) {
|
||||
unsigned int num = 0;
|
||||
unsigned int testflags = flags >> 1;
|
||||
while (testflags) {
|
||||
if (testflags & 1) {
|
||||
num += getSizeOfTrailingDataEntry(ptr, size - num);
|
||||
}
|
||||
testflags >>= 1;
|
||||
}
|
||||
if (flags & 1) {
|
||||
num += (ptr[size - num - 1] & 0x3) + 1;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
unsigned char *parseDRM(unsigned char *data, unsigned int count, unsigned char *pid, unsigned int pidlen) {
|
||||
unsigned int i;
|
||||
unsigned char temp_key_sum = 0;
|
||||
unsigned char *found_key = NULL;
|
||||
unsigned char *keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96";
|
||||
unsigned char temp_key[16];
|
||||
|
||||
memset(temp_key, 0, 16);
|
||||
memcpy(temp_key, pid, 8);
|
||||
PC1(keyvec1, 16, temp_key, temp_key, 16, 0);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
temp_key_sum += temp_key[i];
|
||||
}
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
unsigned char kk[32];
|
||||
vstruct *v = (vstruct*)(data + i * 0x30);
|
||||
kstruct *k = (kstruct*)PC1(temp_key, 16, v->cookie, kk, 32, 1);
|
||||
|
||||
if (v->verification == k->ver && v->cksum[0] == temp_key_sum &&
|
||||
(bswap_l(k->flags) & 0x1F) == 1) {
|
||||
found_key = (unsigned char*)malloc(16);
|
||||
memcpy(found_key, k->finalkey, 16);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return found_key;
|
||||
}
|
||||
|
||||
void freeMobiFile(MobiFile *book) {
|
||||
free(book->hr);
|
||||
free(book->record0);
|
||||
free(book);
|
||||
}
|
||||
|
||||
MobiFile *parseMobiHeader(FILE *f) {
|
||||
unsigned int numRecs, i, magic;
|
||||
MobiFile *book = (MobiFile*)calloc(sizeof(MobiFile), 1);
|
||||
book->f = f;
|
||||
if (fread(&book->pdb, sizeof(PDB), 1, f) != 1) {
|
||||
fprintf(stderr, "Failed to read Palm headers\n");
|
||||
free(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//do BOOKMOBI check
|
||||
if (book->pdb.type != 0x4b4f4f42 || book->pdb.creator != 0x49424f4d) {
|
||||
fprintf(stderr, "Invalid header type or creator\n");
|
||||
free(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->recs = bswap_s(book->pdb.numRecs);
|
||||
|
||||
book->hr = (HeaderRec*)malloc(book->recs * sizeof(HeaderRec));
|
||||
if (fread(book->hr, sizeof(HeaderRec), book->recs, f) != book->recs) {
|
||||
fprintf(stderr, "Failed read of header record\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->record0_offset = bswap_l(book->hr[0].offset);
|
||||
book->record0_size = bswap_l(book->hr[1].offset) - book->record0_offset;
|
||||
|
||||
if (fseek(f, book->record0_offset, SEEK_SET) == -1) {
|
||||
fprintf(stderr, "bad seek to header record offset\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->record0 = (unsigned char*)malloc(book->record0_size);
|
||||
|
||||
if (fread(book->record0, book->record0_size, 1, f) != 1) {
|
||||
fprintf(stderr, "bad read of record0\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->pdh = (PalmDocHeader*)(book->record0);
|
||||
if (bswap_s(book->pdh->encryptionType) != 2) {
|
||||
fprintf(stderr, "MOBI BOOK is not encrypted\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->textRecs = bswap_s(book->pdh->recordCount);
|
||||
|
||||
book->mobi = (MobiHeader*)(book->pdh + 1);
|
||||
if (book->mobi->id != 0x49424f4d) {
|
||||
fprintf(stderr, "MOBI header not found\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->mobiLen = bswap_l(book->mobi->hdrLen);
|
||||
book->extra_data_flags = 0;
|
||||
|
||||
if (book->mobiLen >= 0xe4) {
|
||||
book->extra_data_flags = bswap_s(book->mobi->extra_flags);
|
||||
}
|
||||
|
||||
if ((bswap_l(book->mobi->exthFlags) & 0x40) == 0) {
|
||||
fprintf(stderr, "Missing exth header\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
book->exth = (ExthHeader*)(book->mobiLen + (char*)(book->mobi));
|
||||
if (book->exth->id != 0x48545845) {
|
||||
fprintf(stderr, "EXTH header not found\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//if you want a list of EXTH records, uncomment the following
|
||||
// enumExthRecords(exth);
|
||||
|
||||
book->drmCount = bswap_l(book->mobi->drmCount);
|
||||
|
||||
if (book->drmCount == 0) {
|
||||
fprintf(stderr, "no PIDs found in this file\n");
|
||||
freeMobiFile(book);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return book;
|
||||
}
|
||||
|
||||
int writeMobiOutputFile(MobiFile *book, FILE *out, unsigned char *key,
|
||||
unsigned int drmOffset, unsigned int drm_len) {
|
||||
int i;
|
||||
struct stat statbuf;
|
||||
|
||||
fstat(fileno(book->f), &statbuf);
|
||||
|
||||
// kill the drm keys
|
||||
memset(book->record0 + drmOffset, 0, drm_len);
|
||||
// kill the drm pointers
|
||||
book->mobi->drmOffset = 0xffffffff;
|
||||
book->mobi->drmCount = book->mobi->drmSize = book->mobi->drmFlags = 0;
|
||||
// clear the crypto type
|
||||
book->pdh->encryptionType = 0;
|
||||
|
||||
fwrite(&book->pdb, sizeof(PDB), 1, out);
|
||||
fwrite(book->hr, sizeof(HeaderRec), book->recs, out);
|
||||
fwrite("\x00\x00", 1, 2, out);
|
||||
fwrite(book->record0, book->record0_size, 1, out);
|
||||
|
||||
//need to zero out exth 209 data
|
||||
for (i = 1; i < book->recs; i++) {
|
||||
unsigned int offset = bswap_l(book->hr[i].offset);
|
||||
unsigned int len, extra_size = 0;
|
||||
unsigned char *rec;
|
||||
if (i == (book->recs - 1)) { //last record extends to end of file
|
||||
len = statbuf.st_size - offset;
|
||||
}
|
||||
else {
|
||||
len = bswap_l(book->hr[i + 1].offset) - offset;
|
||||
}
|
||||
//make sure we are at proper offset
|
||||
while (ftell(out) < offset) {
|
||||
fwrite("\x00", 1, 1, out);
|
||||
}
|
||||
rec = (unsigned char *)malloc(len);
|
||||
if (fseek(book->f, offset, SEEK_SET) != 0) {
|
||||
fprintf(stderr, "Failed record seek on input\n");
|
||||
freeMobiFile(book);
|
||||
free(rec);
|
||||
return 0;
|
||||
}
|
||||
if (fread(rec, len, 1, book->f) != 1) {
|
||||
fprintf(stderr, "Failed record read on input\n");
|
||||
freeMobiFile(book);
|
||||
free(rec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (i <= book->textRecs) { //decrypt if necessary
|
||||
extra_size = getSizeOfTrailingDataEntries(rec, len, book->extra_data_flags);
|
||||
PC1(key, 16, rec, rec, len - extra_size, 1);
|
||||
}
|
||||
fwrite(rec, len, 1, out);
|
||||
free(rec);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
147
Kindle_Mobi_Tools/skindle-06/mobi.h
Normal file
147
Kindle_Mobi_Tools/skindle-06/mobi.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __MOBI_H
|
||||
#define __MOBI_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "skinutils.h"
|
||||
|
||||
#pragma pack(2)
|
||||
typedef struct _PDB {
|
||||
char name[32];
|
||||
unsigned short attrib;
|
||||
unsigned short version;
|
||||
unsigned int created;
|
||||
unsigned int modified;
|
||||
unsigned int backup;
|
||||
unsigned int modNum;
|
||||
unsigned int appInfoID;
|
||||
unsigned int sortInfoID;
|
||||
unsigned int type;
|
||||
unsigned int creator;
|
||||
unsigned int uniqueIDseed;
|
||||
unsigned int nextRecordListID;
|
||||
unsigned short numRecs;
|
||||
} PDB;
|
||||
|
||||
typedef struct _HeaderRec {
|
||||
unsigned int offset;
|
||||
unsigned int attribId;
|
||||
} HeaderRec;
|
||||
|
||||
#define attrib(x) ((x)&0xFF)
|
||||
#define id(x) (bswap_l((x) & 0xFFFFFF00))
|
||||
|
||||
typedef struct _PalmDocHeader {
|
||||
unsigned short compression;
|
||||
unsigned short reserverd1;
|
||||
unsigned int textLength;
|
||||
unsigned short recordCount;
|
||||
unsigned short recordSize;
|
||||
unsigned short encryptionType;
|
||||
unsigned short reserved2;
|
||||
} PalmDocHeader;
|
||||
|
||||
|
||||
//checked lengths are 24, 116, 208, 228
|
||||
typedef struct _MobiHeader {
|
||||
unsigned int id;
|
||||
unsigned int hdrLen;
|
||||
unsigned int type;
|
||||
unsigned int encoding;
|
||||
unsigned int uniqueId;
|
||||
unsigned int generator;
|
||||
unsigned char reserved1[40];
|
||||
unsigned int firstNonBookIdx;
|
||||
unsigned int nameOffset;
|
||||
unsigned int nameLength;
|
||||
unsigned int language;
|
||||
unsigned int inputLang;
|
||||
unsigned int outputLang;
|
||||
unsigned int formatVersion;
|
||||
unsigned int firstImageIdx;
|
||||
unsigned char unknown1[16];
|
||||
unsigned int exthFlags;
|
||||
unsigned char unknown2[36];
|
||||
unsigned int drmOffset;
|
||||
unsigned int drmCount;
|
||||
unsigned int drmSize;
|
||||
unsigned int drmFlags;
|
||||
unsigned char unknown3[58];
|
||||
unsigned short extra_flags;
|
||||
} MobiHeader;
|
||||
|
||||
typedef struct _ExthRecHeader {
|
||||
unsigned int type;
|
||||
unsigned int len;
|
||||
} ExthRecHeader;
|
||||
|
||||
typedef struct _ExthHeader {
|
||||
unsigned int id;
|
||||
unsigned int hdrLen;
|
||||
unsigned int recordCount;
|
||||
ExthRecHeader records[1];
|
||||
} ExthHeader;
|
||||
|
||||
typedef struct _vstruct {
|
||||
unsigned int verification;
|
||||
unsigned int size;
|
||||
unsigned int type;
|
||||
unsigned char cksum[4];
|
||||
unsigned char cookie[32];
|
||||
} vstruct;
|
||||
|
||||
typedef struct _kstruct {
|
||||
unsigned int ver;
|
||||
unsigned int flags;
|
||||
unsigned char finalkey[16];
|
||||
unsigned int expiry;
|
||||
unsigned int expiry2;
|
||||
} kstruct;
|
||||
|
||||
typedef struct _MobiFile {
|
||||
FILE *f;
|
||||
PDB pdb;
|
||||
HeaderRec *hr;
|
||||
PalmDocHeader *pdh;
|
||||
MobiHeader *mobi;
|
||||
ExthHeader *exth;
|
||||
unsigned char *record0;
|
||||
unsigned int record0_offset;
|
||||
unsigned int record0_size;
|
||||
unsigned int mobiLen;
|
||||
unsigned int extra_data_flags;
|
||||
unsigned int recs;
|
||||
unsigned int drmCount;
|
||||
unsigned int textRecs;
|
||||
PidList *pids; //extra pids to try from command line
|
||||
} MobiFile;
|
||||
|
||||
unsigned char *getExthData(MobiFile *book, unsigned int type, unsigned int *len);
|
||||
void enumExthRecords(ExthHeader *eh);
|
||||
unsigned char *PC1(unsigned char *key, unsigned int klen, unsigned char *src,
|
||||
unsigned char *dest, unsigned int len, int decryption);
|
||||
unsigned int getSizeOfTrailingDataEntry(unsigned char *ptr, unsigned int size);
|
||||
unsigned int getSizeOfTrailingDataEntries(unsigned char *ptr, unsigned int size, unsigned int flags);
|
||||
unsigned char *parseDRM(unsigned char *data, unsigned int count, unsigned char *pid, unsigned int pidlen);
|
||||
|
||||
void freeMobiFile(MobiFile *book);
|
||||
MobiFile *parseMobiHeader(FILE *f);
|
||||
int writeMobiOutputFile(MobiFile *book, FILE *out, unsigned char *key,
|
||||
unsigned int drmOffset, unsigned int drm_len);
|
||||
|
||||
#endif
|
||||
234
Kindle_Mobi_Tools/skindle-06/sha1.c
Normal file
234
Kindle_Mobi_Tools/skindle-06/sha1.c
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
sha1.c: Implementation of SHA-1 Secure Hash Algorithm-1
|
||||
|
||||
Based upon: NIST FIPS180-1 Secure Hash Algorithm-1
|
||||
http://www.itl.nist.gov/fipspubs/fip180-1.htm
|
||||
|
||||
Non-official Japanese Translation by HIRATA Yasuyuki:
|
||||
http://yasu.asuka.net/translations/SHA-1.html
|
||||
|
||||
Copyright (C) 2002 vi@nwr.jp. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgement in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as beging the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Note:
|
||||
The copyright notice above is copied from md5.h by L. Peter Deutsch
|
||||
<ghost@aladdin.com>. Thank him since I'm not a good speaker of English. :)
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "sha1.h"
|
||||
|
||||
/*
|
||||
* Packing bytes to a word
|
||||
*
|
||||
* Should not assume p is aligned to word boundary
|
||||
*/
|
||||
static sha1_word_t packup(sha1_byte_t *p)
|
||||
{
|
||||
/* Portable, but slow */
|
||||
return p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3] << 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unpacking a word to bytes
|
||||
*
|
||||
* Should not assume p is aligned to word boundary
|
||||
*/
|
||||
static void unpackup(sha1_byte_t *p, sha1_word_t q)
|
||||
{
|
||||
p[0] = (q >> 24) & 0xff;
|
||||
p[1] = (q >> 16) & 0xff;
|
||||
p[2] = (q >> 8) & 0xff;
|
||||
p[3] = (q >> 0) & 0xff;
|
||||
}
|
||||
|
||||
/*
|
||||
* Processing a block
|
||||
*/
|
||||
static void sha1_update_now(sha1_state_s *pms, sha1_byte_t *bp)
|
||||
{
|
||||
sha1_word_t tmp, a, b, c, d, e, w[16+16];
|
||||
int i, s;
|
||||
|
||||
/* pack 64 bytes into 16 words */
|
||||
for(i = 0; i < 16; i++) {
|
||||
w[i] = packup(bp + i * sizeof(sha1_word_t));
|
||||
}
|
||||
memcpy(w + 16, w + 0, sizeof(sha1_word_t) * 16);
|
||||
|
||||
a = pms->sha1_h[0], b = pms->sha1_h[1], c = pms->sha1_h[2], d = pms->sha1_h[3], e = pms->sha1_h[4];
|
||||
|
||||
#define rot(x,n) (((x) << n) | ((x) >> (32-n)))
|
||||
#define f0(b, c, d) ((b&c)|(~b&d))
|
||||
#define f1(b, c, d) (b^c^d)
|
||||
#define f2(b, c, d) ((b&c)|(b&d)|(c&d))
|
||||
#define f3(b, c, d) (b^c^d)
|
||||
#define k0 0x5a827999
|
||||
#define k1 0x6ed9eba1
|
||||
#define k2 0x8f1bbcdc
|
||||
#define k3 0xca62c1d6
|
||||
|
||||
/* t=0-15 */
|
||||
s = 0;
|
||||
for(i = 0; i < 16; i++) {
|
||||
tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=16-19 */
|
||||
for(i = 16; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f0(b, c, d) + e + w[s] + k0;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=20-39 */
|
||||
for(i = 0; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f1(b, c, d) + e + w[s] + k1;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=40-59 */
|
||||
for(i = 0; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f2(b, c, d) + e + w[s] + k2;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
/* t=60-79 */
|
||||
for(i = 0; i < 20; i++) {
|
||||
w[s] = rot(w[s+13] ^ w[s+8] ^ w[s+2] ^ w[s], 1);
|
||||
w[s+16] = w[s];
|
||||
tmp = rot(a, 5) + f3(b, c, d) + e + w[s] + k3;
|
||||
e = d; d = c; c = rot(b, 30); b = a; a = tmp;
|
||||
s = (s + 1) % 16;
|
||||
}
|
||||
|
||||
pms->sha1_h[0] += a, pms->sha1_h[1] += b, pms->sha1_h[2] += c, pms->sha1_h[3] += d, pms->sha1_h[4] += e;
|
||||
}
|
||||
|
||||
/*
|
||||
* Increment sha1_size1, sha1_size2 field of sha1_state_s
|
||||
*/
|
||||
static void incr(sha1_state_s *pms, int v)
|
||||
{
|
||||
sha1_word_t q;
|
||||
|
||||
q = pms->sha1_size1 + v * BITS;
|
||||
if(q < pms->sha1_size1) {
|
||||
pms->sha1_size2++;
|
||||
}
|
||||
pms->sha1_size1 = q;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize sha1_state_s as FIPS specifies
|
||||
*/
|
||||
void sha1_init(sha1_state_s *pms)
|
||||
{
|
||||
memset(pms, 0, sizeof(*pms));
|
||||
pms->sha1_h[0] = 0x67452301; /* Initialize H[0]-H[4] */
|
||||
pms->sha1_h[1] = 0xEFCDAB89;
|
||||
pms->sha1_h[2] = 0x98BADCFE;
|
||||
pms->sha1_h[3] = 0x10325476;
|
||||
pms->sha1_h[4] = 0xC3D2E1F0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fill block and update output when needed
|
||||
*/
|
||||
void sha1_update(sha1_state_s *pms, sha1_byte_t *bufp, int length)
|
||||
{
|
||||
/* Is the buffer partially filled? */
|
||||
if(pms->sha1_count != 0) {
|
||||
if(pms->sha1_count + length >= (signed) sizeof(pms->sha1_buf)) { /* buffer is filled enough */
|
||||
int fil = sizeof(pms->sha1_buf) - pms->sha1_count; /* length to copy */
|
||||
|
||||
memcpy(pms->sha1_buf + pms->sha1_count, bufp, fil);
|
||||
sha1_update_now(pms, pms->sha1_buf);
|
||||
length -= fil;
|
||||
bufp += fil;
|
||||
pms->sha1_count = 0;
|
||||
incr(pms, fil);
|
||||
} else {
|
||||
memcpy(pms->sha1_buf + pms->sha1_count, bufp, length);
|
||||
pms->sha1_count += length;
|
||||
incr(pms, length);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop to update state */
|
||||
for(;;) {
|
||||
if(length < (signed) sizeof(pms->sha1_buf)) { /* Short to fill up the buffer */
|
||||
if(length) {
|
||||
memcpy(pms->sha1_buf, bufp, length);
|
||||
}
|
||||
pms->sha1_count = length;
|
||||
incr(pms, length);
|
||||
break;
|
||||
}
|
||||
sha1_update_now(pms, bufp);
|
||||
length -= sizeof(pms->sha1_buf);
|
||||
bufp += sizeof(pms->sha1_buf);
|
||||
incr(pms, sizeof(pms->sha1_buf));
|
||||
}
|
||||
}
|
||||
|
||||
void sha1_finish(sha1_state_s *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE])
|
||||
{
|
||||
int i;
|
||||
sha1_byte_t buf[1];
|
||||
|
||||
/* fill a bit */
|
||||
buf[0] = 0x80;
|
||||
sha1_update(pms, buf, 1);
|
||||
|
||||
/* Decrement sha1_size1, sha1_size2 */
|
||||
if((pms->sha1_size1 -= BITS) == 0) {
|
||||
pms->sha1_size2--;
|
||||
}
|
||||
|
||||
/* fill zeros */
|
||||
if(pms->sha1_count > (signed) (sizeof(pms->sha1_buf) - 2 * sizeof(sha1_word_t))) {
|
||||
memset(pms->sha1_buf + pms->sha1_count, 0, sizeof(pms->sha1_buf) - pms->sha1_count);
|
||||
sha1_update_now(pms, pms->sha1_buf);
|
||||
pms->sha1_count = 0;
|
||||
}
|
||||
memset(pms->sha1_buf + pms->sha1_count, 0,
|
||||
sizeof(pms->sha1_buf) - pms->sha1_count - sizeof(sha1_word_t) * 2);
|
||||
|
||||
/* fill last length */
|
||||
unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 2, pms->sha1_size2);
|
||||
unpackup(pms->sha1_buf + sizeof(pms->sha1_buf) - sizeof(sha1_word_t) * 1, pms->sha1_size1);
|
||||
|
||||
/* final update */
|
||||
sha1_update_now(pms, pms->sha1_buf);
|
||||
|
||||
/* move hash value to output byte array */
|
||||
for(i = 0; i < (signed) (sizeof(pms->sha1_h)/sizeof(sha1_word_t)); i++) {
|
||||
unpackup(output + i * sizeof(sha1_word_t), pms->sha1_h[i]);
|
||||
}
|
||||
}
|
||||
68
Kindle_Mobi_Tools/skindle-06/sha1.h
Normal file
68
Kindle_Mobi_Tools/skindle-06/sha1.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
sha1.h: Implementation of SHA-1 Secure Hash Algorithm-1
|
||||
|
||||
Based upon: NIST FIPS180-1 Secure Hash Algorithm-1
|
||||
http://www.itl.nist.gov/fipspubs/fip180-1.htm
|
||||
|
||||
Non-official Japanese Translation by HIRATA Yasuyuki:
|
||||
http://yasu.asuka.net/translations/SHA-1.html
|
||||
|
||||
Copyright (C) 2002 vi@nwr.jp. All rights reserved.
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgement in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as beging the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
Note:
|
||||
The copyright notice above is copied from md5.h by L. Petet Deutsch
|
||||
<ghost@aladdin.com>. Thank him since I'm not a good speaker of English. :)
|
||||
*/
|
||||
#ifndef SHA1_H
|
||||
#define SHA1_H
|
||||
|
||||
typedef unsigned int sha1_word_t; /* 32bits unsigned integer */
|
||||
typedef unsigned char sha1_byte_t; /* 8bits unsigned integer */
|
||||
#define BITS 8
|
||||
|
||||
/* Define the state of SHA-1 algorithm */
|
||||
typedef struct {
|
||||
sha1_byte_t sha1_buf[64]; /* 512 bits */
|
||||
int sha1_count; /* How many bytes are used */
|
||||
sha1_word_t sha1_size1; /* Length counter Lower Word */
|
||||
sha1_word_t sha1_size2; /* Length counter Upper Word */
|
||||
sha1_word_t sha1_h[5]; /* Hash output */
|
||||
} sha1_state_s;
|
||||
#define SHA1_OUTPUT_SIZE 20 /* in bytes */
|
||||
|
||||
/* External Functions */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Initialize SHA-1 algorithm */
|
||||
void sha1_init(sha1_state_s *pms);
|
||||
|
||||
/* Append a string to SHA-1 algorithm */
|
||||
void sha1_update(sha1_state_s *pms, sha1_byte_t *input_buffer, int length);
|
||||
|
||||
/* Finish the SHA-1 algorithm and return the hash */
|
||||
void sha1_finish(sha1_state_s *pms, sha1_byte_t output[SHA1_OUTPUT_SIZE]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
461
Kindle_Mobi_Tools/skindle-06/skindle.c
Normal file
461
Kindle_Mobi_Tools/skindle-06/skindle.c
Normal file
@@ -0,0 +1,461 @@
|
||||
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Dependencies: none
|
||||
* build on cygwin:
|
||||
* gcc -o skindle skindle.c md5.c sha1.c b64.c -lCrypt32
|
||||
* or gcc -o skindle skindle.c md5.c sha1.c b64.c -lCrypt32 -mno-cygwin
|
||||
* Under cygwin, you can just type make to build it.
|
||||
* The code should compile with Visual Studio, just add all the files to
|
||||
* a project and add the Crypt32.lib dependency and it should build as a
|
||||
* Win32 console app.
|
||||
*/
|
||||
|
||||
/*
|
||||
* MUST be run on the computer on which KindleForPC is installed
|
||||
* under the account that was used to purchase DRM'ed content.
|
||||
* Requires your kindle.info file which can be found in something like:
|
||||
* <User home>\...\Amazon\Kindle For PC\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}
|
||||
* where ... varies by platform but is "Local Settings\Application Data" on XP
|
||||
*/
|
||||
|
||||
/*
|
||||
What: KindleForPC DRM removal utility to preserve your fair use rights!
|
||||
Why: Fair use is a well established doctrine, and I am no fan of vendor
|
||||
lockin.
|
||||
How: This utility implements the PID extraction, DRM key generation and
|
||||
decryption algorithms employed by the KindleForPC application. This
|
||||
is a stand alone app that does not require you to determine a PID on
|
||||
your own, and it does not need to run KindleForPC in order to extract
|
||||
any data from memory.
|
||||
|
||||
Shoutz: The DarkReverser - thanks for mobidedrm! The last part of this
|
||||
is just a C port of mobidedrm.
|
||||
labba and I<3cabbages for motivating me to do this the right way.
|
||||
You guys shouldn't need to spend all your time responding to all the
|
||||
changes Amazon is going to force you to make in unswindle each time
|
||||
the release a new version.
|
||||
Lawrence Lessig - You are my hero. 'Nuff said.
|
||||
Thumbs down: Disney, MPAA, RIAA - you guys suck. Making billions off
|
||||
of the exploitation of works out of copyright while vigourously
|
||||
pushing copyright extension to prevent others from doing the same
|
||||
is the height of hypocrasy.
|
||||
Congress - you guys suck too. Why you arrogant pricks think you
|
||||
are smarter than the founding fathers is beyond me.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <Wincrypt.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "skinutils.h"
|
||||
#include "cbuf.h"
|
||||
#include "mobi.h"
|
||||
#include "tpz.h"
|
||||
|
||||
#include "zlib.h"
|
||||
|
||||
int processTopaz(FILE *in, char *outFile, int explode, PidList *extraPids) {
|
||||
//had to pile all these up here to please VS2009
|
||||
cbuf *tpzHeaders, *tpzBody;
|
||||
struct stat statbuf;
|
||||
FILE *out;
|
||||
unsigned int i;
|
||||
char *keysRecord, *keysRecordRecord;
|
||||
TopazFile *topaz;
|
||||
char *pid;
|
||||
|
||||
fstat(fileno(in), &statbuf);
|
||||
|
||||
topaz = parseTopazHeader(in);
|
||||
if (topaz == NULL) {
|
||||
fprintf(stderr, "Failed to parse topaz headers\n");
|
||||
return 0;
|
||||
}
|
||||
topaz->pids = extraPids;
|
||||
|
||||
tpzHeaders = b_new(topaz->bodyOffset);
|
||||
tpzBody = b_new(statbuf.st_size);
|
||||
|
||||
parseMetadata(topaz);
|
||||
|
||||
// dumpMap(bookMetadata);
|
||||
|
||||
keysRecord = getMetadata(topaz, "keys");
|
||||
if (keysRecord == NULL) {
|
||||
//fail
|
||||
}
|
||||
keysRecordRecord = getMetadata(topaz, keysRecord);
|
||||
if (keysRecordRecord == NULL) {
|
||||
//fail
|
||||
}
|
||||
|
||||
pid = getBookPid(keysRecord, strlen(keysRecord), keysRecordRecord, strlen(keysRecordRecord));
|
||||
|
||||
if (pid == NULL) {
|
||||
fprintf(stderr, "Failed to extract pid automatically\n");
|
||||
}
|
||||
else {
|
||||
char *title = getMetadata(topaz, "Title");
|
||||
fprintf(stderr, "PID for %s is: %s\n", title ? title : "UNK", pid);
|
||||
}
|
||||
|
||||
/*
|
||||
unique pid is computed as:
|
||||
base64(sha1(idArray . kindleToken . 209_data . 209_tokens))
|
||||
*/
|
||||
|
||||
//
|
||||
// Decrypt book key
|
||||
//
|
||||
|
||||
Payload *dkey = getBookPayloadRecord(topaz, "dkey", 0, 0);
|
||||
|
||||
if (dkey == NULL) {
|
||||
fprintf(stderr, "No dkey record found\n");
|
||||
freeTopazFile(topaz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (pid) {
|
||||
topaz->bookKey = decryptDkeyRecords(dkey, pid);
|
||||
free(pid);
|
||||
}
|
||||
if (topaz->bookKey == NULL) {
|
||||
if (extraPids) {
|
||||
int p;
|
||||
freePayload(dkey);
|
||||
for (p = 0; p < extraPids->numPids; p++) {
|
||||
dkey = getBookPayloadRecord(topaz, "dkey", 0, 0);
|
||||
topaz->bookKey = decryptDkeyRecords(dkey, extraPids->pidList[p]);
|
||||
if (topaz->bookKey) break;
|
||||
}
|
||||
}
|
||||
if (topaz->bookKey == NULL) {
|
||||
fprintf(stderr, "No valid pids available, failed to find DRM key\n");
|
||||
freeTopazFile(topaz);
|
||||
freePayload(dkey);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "Found a DRM key!\n");
|
||||
for (i = 0; i < 8; i++) {
|
||||
fprintf(stderr, "%02x", topaz->bookKey[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
out = fopen(outFile, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "Failed to open output file, quitting\n");
|
||||
freeTopazFile(topaz);
|
||||
freePayload(dkey);
|
||||
return 0;
|
||||
}
|
||||
|
||||
writeTopazOutputFile(topaz, out, tpzHeaders, tpzBody, explode);
|
||||
fwrite(tpzHeaders->buf, tpzHeaders->idx, 1, out);
|
||||
fwrite(tpzBody->buf, tpzBody->idx, 1, out);
|
||||
fclose(out);
|
||||
b_free(tpzHeaders);
|
||||
b_free(tpzBody);
|
||||
|
||||
freePayload(dkey);
|
||||
|
||||
freeTopazFile(topaz);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int processMobi(FILE *prc, char *outFile, PidList *extraPids) {
|
||||
//had to pile all these up here to please VS2009
|
||||
PDB header;
|
||||
cbuf *keyBuf;
|
||||
char *pid;
|
||||
FILE *out;
|
||||
unsigned int i, keyPtrLen;
|
||||
unsigned char *keyPtr;
|
||||
unsigned int drmOffset, drm_len;
|
||||
unsigned char *drm, *found_key = NULL;
|
||||
MobiFile *book;
|
||||
int result;
|
||||
|
||||
book = parseMobiHeader(prc);
|
||||
|
||||
if (book == NULL) {
|
||||
fprintf(stderr, "Failed to read mobi headers\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
book->pids = extraPids;
|
||||
keyPtr = getExthData(book, 209, &keyPtrLen);
|
||||
|
||||
keyBuf = b_new(128);
|
||||
if (keyPtr != NULL) {
|
||||
unsigned int idx;
|
||||
for (idx = 0; idx < keyPtrLen; idx += 5) {
|
||||
unsigned char *rec;
|
||||
unsigned int dlen;
|
||||
unsigned int rtype = bswap_l(*(unsigned int*)(keyPtr + idx + 1));
|
||||
rec = getExthData(book, rtype, &dlen);
|
||||
if (rec != NULL) {
|
||||
b_add_buf(keyBuf, rec, dlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pid = getBookPid(keyPtr, keyPtrLen, keyBuf->buf, keyBuf->idx);
|
||||
|
||||
b_free(keyBuf);
|
||||
|
||||
if (pid == NULL) {
|
||||
fprintf(stderr, "Failed to extract pid automatically\n");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "PID for %s is: %s\n", book->pdb.name, pid);
|
||||
}
|
||||
|
||||
/*
|
||||
unique pid is computed as:
|
||||
base64(sha1(idArray . kindleToken . 209_data . 209_tokens))
|
||||
*/
|
||||
|
||||
drmOffset = bswap_l(book->mobi->drmOffset);
|
||||
|
||||
drm_len = bswap_l(book->mobi->drmSize);
|
||||
drm = book->record0 + drmOffset;
|
||||
|
||||
if (pid) {
|
||||
found_key = parseDRM(drm, book->drmCount, pid, 8);
|
||||
free(pid);
|
||||
}
|
||||
if (found_key == NULL) {
|
||||
if (extraPids) {
|
||||
int p;
|
||||
for (p = 0; p < extraPids->numPids; p++) {
|
||||
found_key = parseDRM(drm, book->drmCount, extraPids->pidList[p], 8);
|
||||
if (found_key) break;
|
||||
}
|
||||
}
|
||||
if (found_key == NULL) {
|
||||
fprintf(stderr, "No valid pids available, failed to find DRM key\n");
|
||||
freeMobiFile(book);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "Found a DRM key!\n");
|
||||
|
||||
out = fopen(outFile, "wb");
|
||||
if (out == NULL) {
|
||||
fprintf(stderr, "Failed to open output file, quitting\n");
|
||||
freeMobiFile(book);
|
||||
free(found_key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = writeMobiOutputFile(book, out, found_key, drmOffset, drm_len);
|
||||
|
||||
fclose(out);
|
||||
if (result == 0) {
|
||||
_unlink(outFile);
|
||||
}
|
||||
freeMobiFile(book);
|
||||
free(found_key);
|
||||
return result;
|
||||
}
|
||||
|
||||
enum {
|
||||
FileTypeUnk,
|
||||
FileTypeMobi,
|
||||
FileTypeTopaz
|
||||
};
|
||||
|
||||
int getFileType(FILE *in) {
|
||||
PDB p;
|
||||
int type = FileTypeUnk;
|
||||
fseek(in, 0, SEEK_SET);
|
||||
fread(&p, sizeof(p), 1, in);
|
||||
if (p.type == 0x4b4f4f42 && p.creator == 0x49424f4d) {
|
||||
type = FileTypeMobi;
|
||||
}
|
||||
else if (strncmp(p.name, "TPZ0", 4) == 0) {
|
||||
type = FileTypeTopaz;
|
||||
}
|
||||
fseek(in, 0, SEEK_SET);
|
||||
return type;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
fprintf(stderr, "usage: ./skindle [-d] [-v] -i <ebook file> -o <output file> [-k kindle.info file] [-p pid]\n");
|
||||
fprintf(stderr, " -d optional, for topaz files only, produce a decompressed output file\n");
|
||||
fprintf(stderr, " -i required name of the input mobi or topaz file\n");
|
||||
fprintf(stderr, " -o required name of the output file to generate\n");
|
||||
fprintf(stderr, " -k optional kindle.info path\n");
|
||||
fprintf(stderr, " -v dump the contents of kindle.info\n");
|
||||
fprintf(stderr, " -p additional PID values to attempt (can specifiy multiple times)\n");
|
||||
}
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
//had to pile all these up here to please VS2009
|
||||
FILE *in;
|
||||
int type, explode = 0;
|
||||
int result = 0;
|
||||
int firstArg = 1;
|
||||
int opt;
|
||||
PidList *pids = NULL;
|
||||
char *infile = NULL, *outfile = NULL, *kinfo = NULL;
|
||||
int dump = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "vdp:i:o:k:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'v':
|
||||
dump = 1;
|
||||
break;
|
||||
case 'd':
|
||||
explode = 1;
|
||||
break;
|
||||
case 'p': {
|
||||
int l = strlen(optarg);
|
||||
if (l == 10) {
|
||||
if (!verifyPidChecksum(optarg)) {
|
||||
fprintf(stderr, "Invalid pid %s, skipping\n", optarg);
|
||||
break;
|
||||
}
|
||||
optarg[8] = 0;
|
||||
}
|
||||
else if (l != 8) {
|
||||
fprintf(stderr, "Invalid pid length for %s, skipping\n", optarg);
|
||||
break;
|
||||
}
|
||||
if (pids == NULL) {
|
||||
pids = (PidList*)malloc(sizeof(PidList));
|
||||
pids->numPids = 1;
|
||||
pids->pidList[0] = optarg;
|
||||
}
|
||||
else {
|
||||
pids = (PidList*)realloc(pids, sizeof(PidList) + pids->numPids * sizeof(unsigned char*));
|
||||
pids->pidList[pids->numPids++] = optarg;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'k':
|
||||
kinfo = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
infile = optarg;
|
||||
break;
|
||||
case 'o':
|
||||
outfile = optarg;
|
||||
break;
|
||||
default: /* '?' */
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (optind != argc) {
|
||||
fprintf(stderr, "Extra options ignored\n");
|
||||
}
|
||||
|
||||
if (!buildKindleMap(kinfo)) {
|
||||
fprintf(stderr, "buildKindleMap failed\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//The following loop dumps the contents of your kindle.info file
|
||||
if (dump) {
|
||||
MapList *ml;
|
||||
// dumpKindleMap();
|
||||
fprintf(stderr, "\nDumping kindle.info contents:\n");
|
||||
for (ml = kindleMap; ml; ml = ml->next) {
|
||||
DATA_BLOB DataIn;
|
||||
DATA_BLOB DataOut;
|
||||
DataIn.pbData = mazamaDecode(ml->value, (int*)&DataIn.cbData);
|
||||
if (CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, 1, &DataOut)) {
|
||||
fprintf(stderr, "%s ==> %s\n", ml->key, translateKindleKey(ml->key));
|
||||
fwrite(DataOut.pbData, DataOut.cbData, 1, stderr);
|
||||
fprintf(stderr, "\n\n");
|
||||
LocalFree(DataOut.pbData);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "CryptUnprotectData failed\n");
|
||||
}
|
||||
free(DataIn.pbData);
|
||||
}
|
||||
}
|
||||
|
||||
if (infile == NULL && outfile == NULL) {
|
||||
//special case, user just wants to see kindle.info
|
||||
freeMap(kindleMap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (infile == NULL) {
|
||||
fprintf(stderr, "Missing input file name\n");
|
||||
usage();
|
||||
freeMap(kindleMap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (outfile == NULL) {
|
||||
fprintf(stderr, "Missing output file name\n");
|
||||
usage();
|
||||
freeMap(kindleMap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
in = fopen(infile, "rb");
|
||||
if (in == NULL) {
|
||||
fprintf(stderr, "%s bad open, quitting\n", infile);
|
||||
freeMap(kindleMap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
type = getFileType(in);
|
||||
if (type == FileTypeTopaz) {
|
||||
result = processTopaz(in, outfile, explode, pids);
|
||||
}
|
||||
else if (type == FileTypeMobi) {
|
||||
result = processMobi(in, outfile, pids);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "%s file type unknown, quitting\n", infile);
|
||||
fclose(in);
|
||||
freeMap(kindleMap);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fclose(in);
|
||||
if (result) {
|
||||
fprintf(stderr, "Success! Enjoy!\n");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "An error occurred, unable to process input file!\n");
|
||||
}
|
||||
|
||||
freeMap(kindleMap);
|
||||
return 0;
|
||||
}
|
||||
BIN
Kindle_Mobi_Tools/skindle-06/skindle.exe
Normal file
BIN
Kindle_Mobi_Tools/skindle-06/skindle.exe
Normal file
Binary file not shown.
539
Kindle_Mobi_Tools/skindle-06/skinutils.c
Normal file
539
Kindle_Mobi_Tools/skindle-06/skinutils.c
Normal file
@@ -0,0 +1,539 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <windows.h>
|
||||
#include <Wincrypt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "skinutils.h"
|
||||
|
||||
/* The kindle.info file created when you install KindleForPC is a set
|
||||
* of key:value pairs delimited by '{'. The keys and values are encoded
|
||||
* in a variety of ways. Keys are the mazama64 encoded md5 hash of the
|
||||
* key name, while values are the mazama64 encoding of the blob returned
|
||||
* by the Windows CryptProtectData function. The use of CryptProtectData
|
||||
* is what locks things to a particular user/machine
|
||||
|
||||
* kindle.info layout
|
||||
|
||||
* Key:AbaZZ6z4a7ZxzLzkZcaqauZMZjZ_Ztz6 ("kindle.account.tokens")
|
||||
* Value: mazama64Encode(CryptProtectData(some sha1 hash))
|
||||
|
||||
* Key:AsAWa4ZJAQaCZ7A3zrZSaZavZMarZFAw ("kindle.cookie.item")
|
||||
* Value: mazama64Encode(CryptProtectData(base64(144 bytes of data)))
|
||||
|
||||
* Key:ZHatAla4a-zTzWA-AvaeAvZQzKZ-agAz ("eulaVersionAccepted")
|
||||
* Value: mazama64Encode(CryptProtectData(kindle version?))
|
||||
|
||||
* Key:ZiajZga7Z9zjZRz7AfZ-zRzUANZNZJzP ("login_date")
|
||||
* Value: mazama64Encode(CryptProtectData(registration date))
|
||||
|
||||
* Key:ZkzeAUA-Z2ZYA2Z_ayA_ahZEATaEAOaG ("kindle.token.item")
|
||||
* Value: mazama64Encode(CryptProtectData(multi-field crypto data))
|
||||
* {enc:xxx}{iv:xxx}{key:xxx}{name:xxx}{serial:xxx}
|
||||
* enc:base64(binary blob)
|
||||
* iv:base64(16 bytes)
|
||||
* key:base64(256 bytes)
|
||||
* name:base64("ADPTokenEncryptionKey")
|
||||
* serial:base64("1")
|
||||
|
||||
* Key:aVzrzRAFZ7aIzmASZOzVzIAGAKawzwaU ("login")
|
||||
* Value: mazama64Encode(CryptProtectData(your amazon email))
|
||||
|
||||
* Key:avalzbzkAcAPAQA5ApZgaOZPzQZzaiaO mazama64Encode(md5("MazamaRandomNumber"))
|
||||
* Value: mazama64Encode(CryptProtectData(mazama32Encode(32 bytes random data)))
|
||||
|
||||
* Key:zgACzqAjZ2zzAmAJa6ZFaZALaYAlZrz- ("kindle.key.item")
|
||||
* Value: mazama64Encode(CryptProtectData(RSA private key)) no password
|
||||
|
||||
* Key:zga-aIANZPzbzfZ1zHZWZcA4afZMZcA_ ("kindle.name.info")
|
||||
* Value: mazama64Encode(CryptProtectData(your name))
|
||||
|
||||
* Key:zlZ9afz1AfAVZjacaqa-ZHa1aIa_ajz7 ("kindle.device.info");
|
||||
* Value: mazama64Encode(CryptProtectData(the name of your kindle))
|
||||
*/
|
||||
|
||||
char *kindleKeys[] = {
|
||||
"AbaZZ6z4a7ZxzLzkZcaqauZMZjZ_Ztz6", "kindle.account.tokens",
|
||||
"AsAWa4ZJAQaCZ7A3zrZSaZavZMarZFAw", "kindle.cookie.item",
|
||||
"ZHatAla4a-zTzWA-AvaeAvZQzKZ-agAz", "eulaVersionAccepted",
|
||||
"ZiajZga7Z9zjZRz7AfZ-zRzUANZNZJzP", "login_date",
|
||||
"ZkzeAUA-Z2ZYA2Z_ayA_ahZEATaEAOaG", "kindle.token.item",
|
||||
"aVzrzRAFZ7aIzmASZOzVzIAGAKawzwaU", "login",
|
||||
"avalzbzkAcAPAQA5ApZgaOZPzQZzaiaO", "MazamaRandomNumber",
|
||||
"zgACzqAjZ2zzAmAJa6ZFaZALaYAlZrz-", "kindle.key.item",
|
||||
"zga-aIANZPzbzfZ1zHZWZcA4afZMZcA_", "kindle.name.info",
|
||||
"zlZ9afz1AfAVZjacaqa-ZHa1aIa_ajz7", "kindle.device.info"
|
||||
};
|
||||
|
||||
MapList *kindleMap;
|
||||
|
||||
unsigned short bswap_s(unsigned short s) {
|
||||
return (s >> 8) | (s << 8);
|
||||
}
|
||||
|
||||
unsigned int bswap_l(unsigned int s) {
|
||||
unsigned int u = bswap_s(s);
|
||||
unsigned int l = bswap_s(s >> 16);
|
||||
return (u << 16) | l;
|
||||
}
|
||||
|
||||
char *translateKindleKey(char *key) {
|
||||
int n = sizeof(kindleKeys) / sizeof(char*);
|
||||
int i;
|
||||
for (i = 0; i < n; i += 2) {
|
||||
if (strcmp(key, kindleKeys[i]) == 0) {
|
||||
return kindleKeys[i + 1];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MapList *findNode(MapList *map, char *key) {
|
||||
MapList *l;
|
||||
for (l = map; l; l = l->next) {
|
||||
if (strcmp(key, l->key) == 0) {
|
||||
return l;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
MapList *findKindleNode(char *key) {
|
||||
return findNode(kindleMap, key);
|
||||
}
|
||||
|
||||
char *getNodeValue(MapList *map, char *key) {
|
||||
MapList *l;
|
||||
for (l = map; l; l = l->next) {
|
||||
if (strcmp(key, l->key) == 0) {
|
||||
return l->value;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *getKindleValue(char *key) {
|
||||
return getNodeValue(kindleMap, key);
|
||||
}
|
||||
|
||||
MapList *addMapNode(MapList *map, char *key, char *value) {
|
||||
MapList *ml;
|
||||
ml = findNode(map, key);
|
||||
if (ml) {
|
||||
free(ml->value);
|
||||
ml->value = value;
|
||||
return map;
|
||||
}
|
||||
else {
|
||||
ml = (MapList*)malloc(sizeof(MapList));
|
||||
ml->key = key;
|
||||
ml->value = value;
|
||||
ml->next = map;
|
||||
return ml;
|
||||
}
|
||||
}
|
||||
|
||||
void dumpMap(MapList *m) {
|
||||
MapList *l;
|
||||
for (l = m; l; l = l->next) {
|
||||
fprintf(stderr, "%s:%s\n", l->key, l->value);
|
||||
}
|
||||
}
|
||||
|
||||
void freeMap(MapList *m) {
|
||||
MapList *n;
|
||||
while (m) {
|
||||
n = m;
|
||||
m = m->next;
|
||||
free(n->key);
|
||||
free(n->value);
|
||||
free(n);
|
||||
}
|
||||
}
|
||||
|
||||
void parseLine(char *line) {
|
||||
char *colon = strchr(line, ':');
|
||||
if (colon) {
|
||||
char *key, *value;
|
||||
int len = colon - line;
|
||||
key = (char*)malloc(len + 1);
|
||||
*colon++ = 0;
|
||||
strcpy(key, line);
|
||||
len = strlen(colon);
|
||||
value = (char*)malloc(len + 1);
|
||||
strcpy(value, colon);
|
||||
value[len] = 0;
|
||||
kindleMap = addMapNode(kindleMap, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
void dumpKindleMap() {
|
||||
dumpMap(kindleMap);
|
||||
}
|
||||
|
||||
int buildKindleMap(char *infoFile) {
|
||||
int result = 0;
|
||||
struct stat statbuf;
|
||||
char ki[512];
|
||||
DWORD len = sizeof(ki);
|
||||
if (infoFile == NULL) {
|
||||
HKEY regkey;
|
||||
fprintf(stderr, "Attempting to locate kindle.info\n");
|
||||
if (RegOpenKey(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders\\", ®key) != ERROR_SUCCESS) {
|
||||
fprintf(stderr, "Unable to locate kindle.info, please specify path on command line\n");
|
||||
return result;
|
||||
}
|
||||
|
||||
// if (RegGetValue(regkey, "Local AppData", NULL, NULL, ki, &len) != ERROR_SUCCESS) {
|
||||
if (RegQueryValueEx(regkey, "Local AppData", NULL, NULL, ki, &len) != ERROR_SUCCESS) {
|
||||
RegCloseKey(regkey);
|
||||
fprintf(stderr, "Unable to locate kindle.info, please specify path on command line\n");
|
||||
return result;
|
||||
}
|
||||
ki[len] = 0;
|
||||
strncat(ki, "\\Amazon\\Kindle For PC\\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}\\kindle.info", sizeof(ki) - 1 - strlen(ki));
|
||||
infoFile = ki;
|
||||
fprintf(stderr, "Found kindle.info location\n");
|
||||
}
|
||||
if (stat(infoFile, &statbuf) == 0) {
|
||||
FILE *fd = fopen(infoFile, "rb");
|
||||
char *infoBuf = (char*)malloc(statbuf.st_size + 1);
|
||||
infoBuf[statbuf.st_size] = 0;
|
||||
if (fread(infoBuf, statbuf.st_size, 1, fd) == 1) {
|
||||
char *end = infoBuf + statbuf.st_size;
|
||||
char *b = infoBuf, *e;
|
||||
while (e = strchr(b, '{')) {
|
||||
*e = 0;
|
||||
if ((e - b) > 2) {
|
||||
parseLine(b);
|
||||
}
|
||||
e++;
|
||||
b = e;
|
||||
}
|
||||
if (b < end) {
|
||||
parseLine(b);
|
||||
}
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "short read on info file\n");
|
||||
}
|
||||
free(infoBuf);
|
||||
fclose(fd);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int crc_table[256];
|
||||
|
||||
void png_crc_table_init() {
|
||||
unsigned int i;
|
||||
if (crc_table[255]) return;
|
||||
for (i = 0; i < 256; i++) {
|
||||
unsigned int n = i;
|
||||
unsigned int j;
|
||||
for (j = 0; j < 8; j++) {
|
||||
if (n & 1) {
|
||||
n = 0xEDB88320 ^ (n >> 1);
|
||||
}
|
||||
else {
|
||||
n >>= 1;
|
||||
}
|
||||
}
|
||||
crc_table[i] = n;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int do_crc(unsigned char *input, unsigned int len) {
|
||||
unsigned int crc = 0;
|
||||
unsigned int i;
|
||||
png_crc_table_init();
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned int v = (input[i] ^ crc) & 0xff;
|
||||
crc = crc_table[v] ^ (crc >> 8);
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
char *decodeString = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
|
||||
|
||||
void doPngDecode(unsigned char *input, unsigned int len, unsigned char *output) {
|
||||
// unsigned int crc_table[256];
|
||||
unsigned int crc, i, x = 0;
|
||||
unsigned int *out = (unsigned int*)output;
|
||||
crc = bswap_l(do_crc(input, len));
|
||||
memset(output, 0, 8);
|
||||
for (i = 0; i < len; i++) {
|
||||
output[x++] ^= input[i];
|
||||
if (x == 8) x = 0;
|
||||
}
|
||||
out[0] ^= crc;
|
||||
out[1] ^= crc;
|
||||
for (i = 0; i < 8; i++) {
|
||||
unsigned char v = output[i];
|
||||
output[i] = decodeString[((((v >> 5) & 3) ^ v) & 0x1F) + (v >> 7)];
|
||||
}
|
||||
}
|
||||
|
||||
static char *string_32 = "n5Pr6St7Uv8Wx9YzAb0Cd1Ef2Gh3Jk4M";
|
||||
static char *string_64 = "AaZzB0bYyCc1XxDdW2wEeVv3FfUuG4g-TtHh5SsIiR6rJjQq7KkPpL8lOoMm9Nn_";
|
||||
|
||||
char *mazamaEncode32(unsigned char *input, unsigned int len) {
|
||||
return mazamaEncode(input, len, 32);
|
||||
}
|
||||
|
||||
char *mazamaEncode64(unsigned char *input, unsigned int len) {
|
||||
return mazamaEncode(input, len, 64);
|
||||
}
|
||||
|
||||
char *mazamaEncode(unsigned char *input, unsigned int len, unsigned char choice) {
|
||||
unsigned int i;
|
||||
char *enc, *out;
|
||||
if (choice == 0x20) enc = string_32;
|
||||
else if (choice == 0x40) enc = string_64;
|
||||
else return NULL;
|
||||
out = (char*)malloc(len * 2 + 1);
|
||||
out[len * 2] = 0;
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned char v = input[i] + 128;
|
||||
unsigned char q = v / choice;
|
||||
unsigned char m = v % choice;
|
||||
out[i * 2] = enc[q];
|
||||
out[i * 2 + 1] = enc[m];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
unsigned char *mazamaDecode(char *input, int *outlen) {
|
||||
unsigned char *out;
|
||||
int len = strlen(input);
|
||||
char *dec = NULL;
|
||||
int i, choice = 0x20;
|
||||
*outlen = 0;
|
||||
for (i = 0; i < 8 && i < len; i++) {
|
||||
if (*input == string_32[i]) {
|
||||
dec = string_32;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dec == NULL) {
|
||||
for (i = 0; i < 4 && i < len; i++) {
|
||||
if (*input == string_64[i]) {
|
||||
dec = string_64;
|
||||
choice = 0x40;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
out = (unsigned char*)malloc(len / 2 + 1);
|
||||
out[len / 2] = 0;
|
||||
for (i = 0; i < len; i += 2) {
|
||||
int q, m, v;
|
||||
char *p = strchr(dec, input[i]);
|
||||
if (p == NULL) break;
|
||||
q = p - dec;
|
||||
p = strchr(dec, input[i + 1]);
|
||||
if (p == NULL) break;
|
||||
m = p - dec;
|
||||
v = (choice * q + m) - 128;
|
||||
out[(*outlen)++] = (unsigned char)v;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifndef HEADER_MD5_H
|
||||
|
||||
void md5(unsigned char *in, int len, unsigned char *md) {
|
||||
MD5_CTX s;
|
||||
MD5_Init(&s);
|
||||
MD5_Update(&s, in, len);
|
||||
MD5_Final(md, &s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HEADER_SHA_H
|
||||
|
||||
void sha1(unsigned char *in, int len, unsigned char *md) {
|
||||
SHA_CTX s;
|
||||
SHA1_Init(&s);
|
||||
SHA1_Update(&s, in, len);
|
||||
SHA1_Final(md, &s);
|
||||
}
|
||||
#endif
|
||||
|
||||
char *getBookPid(unsigned char *keys, unsigned int klen, unsigned char *keysValue, unsigned int kvlen) {
|
||||
unsigned char *vsn, *username, *mrn_key, *kat_key, *pid;
|
||||
char drive[256];
|
||||
char name[256];
|
||||
DWORD nlen = sizeof(name);
|
||||
char *d;
|
||||
char volumeName[256];
|
||||
DWORD volumeSerialNumber;
|
||||
char fileSystemNameBuffer[256];
|
||||
char volumeID[32];
|
||||
unsigned char md5sum[MD5_DIGEST_LENGTH];
|
||||
unsigned char sha1sum[SHA_DIGEST_LENGTH];
|
||||
SHA_CTX sha1_ctx;
|
||||
char *mv;
|
||||
|
||||
if (GetUserName(name, &nlen) == 0) {
|
||||
fprintf(stderr, "GetUserName failed\n");
|
||||
return NULL;
|
||||
}
|
||||
fprintf(stderr, "Using UserName = \"%s\"\n", name);
|
||||
|
||||
d = getenv("SystemDrive");
|
||||
if (d) {
|
||||
strcpy(drive, d);
|
||||
strcat(drive, "\\");
|
||||
}
|
||||
else {
|
||||
strcpy(drive, "c:\\");
|
||||
}
|
||||
fprintf(stderr, "Using SystemDrive = \"%s\"\n", drive);
|
||||
if (GetVolumeInformation(drive, volumeName, sizeof(volumeName), &volumeSerialNumber,
|
||||
NULL, NULL, fileSystemNameBuffer, sizeof(fileSystemNameBuffer))) {
|
||||
sprintf(volumeID, "%u", volumeSerialNumber);
|
||||
}
|
||||
else {
|
||||
strcpy(volumeID, "9999999999");
|
||||
}
|
||||
fprintf(stderr, "Using VolumeSerialNumber = \"%s\"\n", volumeID);
|
||||
MD5(volumeID, strlen(volumeID), md5sum);
|
||||
vsn = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x20);
|
||||
|
||||
MD5(name, strlen(name), md5sum);
|
||||
username = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x20);
|
||||
|
||||
MD5("MazamaRandomNumber", 18, md5sum);
|
||||
mrn_key = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x40);
|
||||
|
||||
MD5("kindle.account.tokens", 21, md5sum);
|
||||
kat_key = mazamaEncode(md5sum, MD5_DIGEST_LENGTH, 0x40);
|
||||
|
||||
SHA1_Init(&sha1_ctx);
|
||||
|
||||
mv = getKindleValue(mrn_key);
|
||||
if (mv) {
|
||||
DATA_BLOB DataIn;
|
||||
DATA_BLOB DataOut;
|
||||
DataIn.pbData = mazamaDecode(mv, (int*)&DataIn.cbData);
|
||||
if (CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, 1, &DataOut)) {
|
||||
char *devId = (char*)malloc(DataOut.cbData + 4 * MD5_DIGEST_LENGTH + 1);
|
||||
char *finalDevId;
|
||||
unsigned char pidbuf[10];
|
||||
|
||||
// fprintf(stderr, "CryptUnprotectData success\n");
|
||||
// fwrite(DataOut.pbData, DataOut.cbData, 1, stderr);
|
||||
// fprintf(stderr, "\n");
|
||||
|
||||
memcpy(devId, DataOut.pbData, DataOut.cbData);
|
||||
strcpy(devId + DataOut.cbData, vsn);
|
||||
strcat(devId + DataOut.cbData, username);
|
||||
|
||||
// fprintf(stderr, "Computing sha1 over %d bytes\n", DataOut.cbData + 4 * MD5_DIGEST_LENGTH);
|
||||
sha1(devId, DataOut.cbData + 4 * MD5_DIGEST_LENGTH, sha1sum);
|
||||
finalDevId = mazamaEncode(sha1sum, SHA_DIGEST_LENGTH, 0x20);
|
||||
// fprintf(stderr, "finalDevId: %s\n", finalDevId);
|
||||
|
||||
SHA1_Update(&sha1_ctx, finalDevId, strlen(finalDevId));
|
||||
|
||||
pidbuf[8] = 0;
|
||||
doPngDecode(finalDevId, 4, (unsigned char*)pidbuf);
|
||||
fprintf(stderr, "Device PID: %s\n", pidbuf);
|
||||
|
||||
LocalFree(DataOut.pbData);
|
||||
free(devId);
|
||||
free(finalDevId);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "CryptUnprotectData failed, quitting\n");
|
||||
free(kat_key);
|
||||
free(mrn_key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(DataIn.pbData);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Failed to find map node: %s\n", mrn_key);
|
||||
}
|
||||
|
||||
mv = getKindleValue(kat_key);
|
||||
if (mv) {
|
||||
DATA_BLOB DataIn;
|
||||
DATA_BLOB DataOut;
|
||||
DataIn.pbData = mazamaDecode(mv, (int*)&DataIn.cbData);
|
||||
if (CryptUnprotectData(&DataIn, NULL, NULL, NULL, NULL, 1, &DataOut)) {
|
||||
// fprintf(stderr, "CryptUnprotectData success\n");
|
||||
// fwrite(DataOut.pbData, DataOut.cbData, 1, stderr);
|
||||
// fprintf(stderr, "\n");
|
||||
|
||||
SHA1_Update(&sha1_ctx, DataOut.pbData, DataOut.cbData);
|
||||
|
||||
LocalFree(DataOut.pbData);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "CryptUnprotectData failed, quitting\n");
|
||||
free(kat_key);
|
||||
free(mrn_key);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
free(DataIn.pbData);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Failed to find map node: %s\n", kat_key);
|
||||
}
|
||||
|
||||
SHA1_Update(&sha1_ctx, keys, klen);
|
||||
SHA1_Update(&sha1_ctx, keysValue, kvlen);
|
||||
SHA1_Final(sha1sum, &sha1_ctx);
|
||||
|
||||
pid = (char*)malloc(SHA_DIGEST_LENGTH * 2);
|
||||
base64(sha1sum, SHA_DIGEST_LENGTH, pid);
|
||||
|
||||
pid[8] = 0;
|
||||
|
||||
free(mrn_key);
|
||||
free(kat_key);
|
||||
free(vsn);
|
||||
free(username);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static char *letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
|
||||
|
||||
int verifyPidChecksum(char *pid) {
|
||||
int l = strlen(letters);
|
||||
unsigned int crc = ~do_crc(pid, 8);
|
||||
unsigned char b;
|
||||
crc = crc ^ (crc >> 16);
|
||||
b = crc & 0xff;
|
||||
if (pid[8] != letters[((b / l) ^ (b % l)) % l]) return 0;
|
||||
crc >>= 8;
|
||||
b = crc & 0xff;
|
||||
if (pid[9] != letters[((b / l) ^ (b % l)) % l]) return 0;
|
||||
return 1;
|
||||
}
|
||||
100
Kindle_Mobi_Tools/skindle-06/skinutils.h
Normal file
100
Kindle_Mobi_Tools/skindle-06/skinutils.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __SKINUTILS_H
|
||||
#define __SKINUTILS_H
|
||||
|
||||
typedef struct _PidList {
|
||||
unsigned int numPids;
|
||||
char *pidList[1]; //extra pids to try from command line
|
||||
} PidList;
|
||||
|
||||
typedef struct _MapList {
|
||||
char *key;
|
||||
char *value;
|
||||
struct _MapList *next;
|
||||
} MapList;
|
||||
|
||||
extern MapList *kindleMap;
|
||||
|
||||
unsigned int base64(unsigned char *inbuf, unsigned int len, unsigned char *outbuf);
|
||||
|
||||
unsigned short bswap_s(unsigned short s);
|
||||
unsigned int bswap_l(unsigned int s);
|
||||
|
||||
char *translateKindleKey(char *key);
|
||||
MapList *findNode(MapList *map, char *key);
|
||||
MapList *findKindleNode(char *key);
|
||||
|
||||
//don't free the result of getNodeValue;
|
||||
char *getNodeValue(MapList *map, char *key);
|
||||
char *getKindleValue(char *key);
|
||||
|
||||
MapList *addMapNode(MapList *map, char *key, char *value);
|
||||
void dumpMap(MapList *m);
|
||||
|
||||
void freeMap(MapList *m);
|
||||
|
||||
int buildKindleMap(char *infoFile);
|
||||
void dumpKindleMap();
|
||||
|
||||
//void png_crc_table_init(unsigned int *crc_table);
|
||||
unsigned int do_crc(unsigned char *input, unsigned int len);
|
||||
void doPngDecode(unsigned char *input, unsigned int len, unsigned char *output);
|
||||
|
||||
char *mazamaEncode(unsigned char *input, unsigned int len, unsigned char choice);
|
||||
char *mazamaEncode32(unsigned char *input, unsigned int len);
|
||||
char *mazamaEncode64(unsigned char *input, unsigned int len);
|
||||
|
||||
unsigned char *mazamaDecode(char *input, int *outlen);
|
||||
|
||||
int verifyPidChecksum(char *pid);
|
||||
|
||||
//If you prefer to use openssl uncomment the following
|
||||
//#include <openssl/sha.h>
|
||||
//#include <openssl/md5.h>
|
||||
|
||||
#ifndef HEADER_MD5_H
|
||||
#include "md5.h"
|
||||
|
||||
#define MD5_DIGEST_LENGTH 16
|
||||
|
||||
#define MD5_CTX md5_state_t
|
||||
#define MD5_Init md5_init
|
||||
#define MD5_Update md5_append
|
||||
#define MD5_Final(x, y) md5_finish(y, x)
|
||||
#define MD5 md5
|
||||
|
||||
void md5(unsigned char *in, int len, unsigned char *md);
|
||||
#endif
|
||||
|
||||
#ifndef HEADER_SHA_H
|
||||
|
||||
#include "sha1.h"
|
||||
|
||||
#define SHA_DIGEST_LENGTH 20
|
||||
#define SHA_CTX sha1_state_s
|
||||
#define SHA1_Init sha1_init
|
||||
#define SHA1_Update sha1_update
|
||||
#define SHA1_Final(x, y) sha1_finish(y, x)
|
||||
#define SHA1 sha1
|
||||
|
||||
void sha1(unsigned char *in, int len, unsigned char *md);
|
||||
#endif
|
||||
|
||||
char *getBookPid(unsigned char *keys, unsigned int klen, unsigned char *keysValue, unsigned int kvlen);
|
||||
|
||||
#endif
|
||||
504
Kindle_Mobi_Tools/skindle-06/tpz.c
Normal file
504
Kindle_Mobi_Tools/skindle-06/tpz.c
Normal file
@@ -0,0 +1,504 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "skinutils.h"
|
||||
#include "cbuf.h"
|
||||
#include "tpz.h"
|
||||
#include "zlib.h"
|
||||
|
||||
//
|
||||
// Context initialisation for the Topaz Crypto
|
||||
//
|
||||
void topazCryptoInit(TpzCtx *ctx, unsigned char *key, int klen) {
|
||||
int i = 0;
|
||||
ctx->v[0] = 0x0CAFFE19E;
|
||||
|
||||
for (i = 0; i < klen; i++) {
|
||||
ctx->v[1] = ctx->v[0];
|
||||
ctx->v[0] = ((ctx->v[0] >> 2) * (ctx->v[0] >> 7)) ^
|
||||
(key[i] * key[i] * 0x0F902007);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// decrypt data with the context prepared by topazCryptoInit()
|
||||
//
|
||||
|
||||
void topazCryptoDecrypt(TpzCtx *ctx, unsigned char *in, unsigned char *out, int len) {
|
||||
unsigned int ctx1 = ctx->v[0];
|
||||
unsigned int ctx2 = ctx->v[1];
|
||||
int i;
|
||||
for (i = 0; i < len; i++) {
|
||||
unsigned char m = in[i] ^ (ctx1 >> 3) ^ (ctx2 << 3);
|
||||
ctx2 = ctx1;
|
||||
ctx1 = ((ctx1 >> 2) * (ctx1 >> 7)) ^ (m * m * 0x0F902007);
|
||||
out[i] = m;
|
||||
}
|
||||
}
|
||||
|
||||
int bookReadEncodedNumber(FILE *f) {
|
||||
int flag = 0;
|
||||
int data = fgetc(f);
|
||||
if (data == 0xFF) { //negative number flag
|
||||
flag = 1;
|
||||
data = fgetc(f);
|
||||
}
|
||||
if (data >= 0x80) {
|
||||
int datax = data & 0x7F;
|
||||
while (data >= 0x80) {
|
||||
data = fgetc(f);
|
||||
datax = (datax << 7) + (data & 0x7F);
|
||||
}
|
||||
data = datax;
|
||||
}
|
||||
|
||||
if (flag) {
|
||||
data = -data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
//
|
||||
// Encode a number in 7 bit format
|
||||
//
|
||||
|
||||
int encodeNumber(int number, unsigned char *out) {
|
||||
unsigned char *b = out;
|
||||
unsigned char flag = 0;
|
||||
int len;
|
||||
int neg = number < 0;
|
||||
|
||||
if (neg) {
|
||||
number = -number + 1;
|
||||
}
|
||||
|
||||
do {
|
||||
*b++ = (number & 0x7F) | flag;
|
||||
number >>= 7;
|
||||
flag = 0x80;
|
||||
} while (number);
|
||||
|
||||
if (neg) {
|
||||
*b++ = 0xFF;
|
||||
}
|
||||
len = b - out;
|
||||
b--;
|
||||
while (out < b) {
|
||||
unsigned char v = *out;
|
||||
*out++ = *b;
|
||||
*b-- = v;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
//
|
||||
// Get a length prefixed string from the file
|
||||
//
|
||||
|
||||
char *bookReadString(FILE *f) {
|
||||
int len = bookReadEncodedNumber(f);
|
||||
char *s = (char*)malloc(len + 1);
|
||||
s[len] = 0;
|
||||
if (fread(s, 1, len, f) != len) {
|
||||
fprintf(stderr, "String read failed at filepos %x\n", ftell(f));
|
||||
free(s);
|
||||
s = NULL;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
//
|
||||
// Read and return the data of one header record at the current book file position [[offset,compressedLength,decompressedLength],...]
|
||||
//
|
||||
|
||||
Record *bookReadHeaderRecordData(FILE *f) {
|
||||
int nbValues = bookReadEncodedNumber(f);
|
||||
Record *result = NULL;
|
||||
Record *tail = NULL;
|
||||
unsigned int i;
|
||||
if (nbValues == -1) {
|
||||
fprintf(stderr, "Parse Error : EOF encountered\n");
|
||||
return NULL;
|
||||
}
|
||||
for (i = 0; i < nbValues; i++) {
|
||||
Record *r = (Record*)malloc(sizeof(Record));
|
||||
r->offset = bookReadEncodedNumber(f);
|
||||
r->length = bookReadEncodedNumber(f);
|
||||
r->compressed = bookReadEncodedNumber(f);
|
||||
r->next = NULL;
|
||||
if (result == NULL) {
|
||||
result = r;
|
||||
}
|
||||
else {
|
||||
tail->next = r;
|
||||
}
|
||||
tail = r;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Read and parse one header record at the current book file position and return the associated data [[offset,compressedLength,decompressedLength],...]
|
||||
//
|
||||
|
||||
void freeRecordList(Record *r) {
|
||||
Record *n;
|
||||
while (r) {
|
||||
n = r;
|
||||
r = r->next;
|
||||
free(n);
|
||||
}
|
||||
}
|
||||
|
||||
void freeHeaderList(HeaderRecord *r) {
|
||||
HeaderRecord *n;
|
||||
while (r) {
|
||||
free(r->tag);
|
||||
freeRecordList(r->rec);
|
||||
n = r;
|
||||
r = r->next;
|
||||
free(n);
|
||||
}
|
||||
}
|
||||
|
||||
void freeTopazFile(TopazFile *t) {
|
||||
freeHeaderList(t->hdrs);
|
||||
freeMap(t->metadata);
|
||||
free(t);
|
||||
}
|
||||
|
||||
HeaderRecord *parseTopazHeaderRecord(FILE *f) {
|
||||
char *tag;
|
||||
Record *record;
|
||||
if (fgetc(f) != 0x63) {
|
||||
fprintf(stderr, "Parse Error : Invalid Header at 0x%x\n", ftell(f) - 1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tag = bookReadString(f);
|
||||
record = bookReadHeaderRecordData(f);
|
||||
if (tag && record) {
|
||||
HeaderRecord *r = (HeaderRecord*)malloc(sizeof(Record));
|
||||
r->tag = tag;
|
||||
r->rec = record;
|
||||
r->next = NULL;
|
||||
return r;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Parse the header of a Topaz file, get all the header records and the offset for the payload
|
||||
//
|
||||
|
||||
HeaderRecord *addRecord(HeaderRecord *head, HeaderRecord *r) {
|
||||
HeaderRecord *i;
|
||||
for (i = head; i; i = i->next) {
|
||||
if (i->next == NULL) {
|
||||
i->next = r;
|
||||
return head;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
TopazFile *parseTopazHeader(FILE *f) {
|
||||
unsigned int numRecs, i, magic;
|
||||
TopazFile *tpz;
|
||||
if (fread(&magic, sizeof(magic), 1, f) != 1) {
|
||||
fprintf(stderr, "Failed to read file magic\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (magic != 0x305a5054) {
|
||||
fprintf(stderr, "Parse Error : Invalid Header, not a Topaz file");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
numRecs = fgetc(f);
|
||||
|
||||
tpz = (TopazFile*)calloc(sizeof(TopazFile), 1);
|
||||
tpz->f = f;
|
||||
|
||||
for (i = 0; i < numRecs; i++) {
|
||||
HeaderRecord *result = parseTopazHeaderRecord(f);
|
||||
if (result == NULL) {
|
||||
break;
|
||||
}
|
||||
tpz->hdrs = addRecord(tpz->hdrs, result);
|
||||
}
|
||||
|
||||
if (fgetc(f) != 0x64) {
|
||||
fprintf(stderr, "Parse Error : Invalid Header end at pos 0x%x\n", ftell(f) - 1);
|
||||
//empty list
|
||||
freeTopazFile(tpz);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tpz->bodyOffset = ftell(f);
|
||||
return tpz;
|
||||
}
|
||||
|
||||
HeaderRecord *findHeader(TopazFile *tpz, char *tag) {
|
||||
HeaderRecord *hr;
|
||||
for (hr = tpz->hdrs; hr; hr = hr->next) {
|
||||
if (strcmp(hr->tag, tag) == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
void freePayload(Payload *p) {
|
||||
free(p->blob);
|
||||
free(p);
|
||||
}
|
||||
|
||||
//
|
||||
//Get a record in the book payload, given its name and index. If necessary the record is decrypted. The record is not decompressed
|
||||
//
|
||||
Payload *getBookPayloadRecord(TopazFile *t, char *name, int index, int explode) {
|
||||
int encrypted = 0;
|
||||
int recordOffset, i, recordIndex;
|
||||
Record *r;
|
||||
int fileSize;
|
||||
char *tag;
|
||||
Payload *p;
|
||||
off_t fileOffset;
|
||||
HeaderRecord *hr = findHeader(t, name);
|
||||
|
||||
if (hr == NULL) {
|
||||
fprintf(stderr, "Parse Error : Invalid Record, record %s not found\n", name);
|
||||
return NULL;
|
||||
}
|
||||
r = hr->rec;
|
||||
for (i = 0; r && i < index; i++) {
|
||||
r = r->next;
|
||||
}
|
||||
if (r == NULL) {
|
||||
fprintf(stderr, "Parse Error : Invalid Record, record %s:%d not found\n", name, index);
|
||||
return NULL;
|
||||
}
|
||||
recordOffset = r->offset;
|
||||
|
||||
if (fseek(t->f, t->bodyOffset + recordOffset, SEEK_SET) == -1) {
|
||||
fprintf(stderr, "Parse Error : Invalid Record offset, record %s:%d\n", name, index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
tag = bookReadString(t->f);
|
||||
if (strcmp(tag, name)) {
|
||||
fprintf(stderr, "Parse Error : Invalid Record offset, record %s:%d name doesn't match\n", name, index);
|
||||
return NULL;
|
||||
}
|
||||
recordIndex = bookReadEncodedNumber(t->f);
|
||||
|
||||
if (recordIndex < 0) {
|
||||
encrypted = 1;
|
||||
recordIndex = -recordIndex - 1;
|
||||
}
|
||||
|
||||
if (recordIndex != index) {
|
||||
fprintf(stderr, "Parse Error : Invalid Record offset, record %s:%d index doesn't match\n", name, index);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fileSize = r->compressed ? r->compressed : r->length;
|
||||
p = (Payload*)malloc(sizeof(Payload));
|
||||
p->blob = (unsigned char*)malloc(fileSize);
|
||||
p->len = fileSize;
|
||||
p->name = name;
|
||||
p->index = index;
|
||||
fileOffset = ftell(t->f);
|
||||
if (fread(p->blob, fileSize, 1, t->f) != 1) {
|
||||
freePayload(p);
|
||||
fprintf(stderr, "Parse Error : Failed payload read of record %s:%d offset 0x%x:0x%x\n", name, index, fileOffset, fileSize);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (encrypted) {
|
||||
TpzCtx ctx;
|
||||
topazCryptoInit(&ctx, t->bookKey, 8);
|
||||
topazCryptoDecrypt(&ctx, p->blob, p->blob, p->len);
|
||||
}
|
||||
|
||||
if (r->compressed && explode) {
|
||||
unsigned char *db = (unsigned char *)malloc(r->length);
|
||||
uLongf dl = r->length;
|
||||
switch (uncompress(db, &dl, p->blob, p->len)) {
|
||||
case Z_OK:
|
||||
free(p->blob);
|
||||
p->blob = db;
|
||||
p->len = dl;
|
||||
break;
|
||||
case Z_MEM_ERROR:
|
||||
free(db);
|
||||
fprintf(stderr, "out of memory\n");
|
||||
break;
|
||||
case Z_BUF_ERROR:
|
||||
free(db);
|
||||
fprintf(stderr, "output buffer wasn't large enough!\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
//
|
||||
// Parse the metadata record from the book payload and return a list of [key,values]
|
||||
//
|
||||
|
||||
char *getMetadata(TopazFile *t, char *key) {
|
||||
return getNodeValue(t->metadata, key);
|
||||
}
|
||||
|
||||
void parseMetadata(TopazFile *t) {
|
||||
char *tag;
|
||||
int flags, nbRecords, i;
|
||||
HeaderRecord *hr = findHeader(t, "metadata");
|
||||
|
||||
fseek(t->f, t->bodyOffset + hr->rec->offset, SEEK_SET);
|
||||
tag = bookReadString(t->f);
|
||||
if (strcmp(tag, "metadata")) {
|
||||
//raise CMBDTCFatal("Parse Error : Record Names Don't Match")
|
||||
return;
|
||||
}
|
||||
|
||||
flags = fgetc(t->f);
|
||||
nbRecords = bookReadEncodedNumber(t->f);
|
||||
|
||||
for (i = 0; i < nbRecords; i++) {
|
||||
char *key = bookReadString(t->f);
|
||||
char *value = bookReadString(t->f);
|
||||
t->metadata = addMapNode(t->metadata, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Decrypt a payload record with the PID
|
||||
//
|
||||
|
||||
void decryptRecord(unsigned char *in, int len, unsigned char *out, unsigned char *PID) {
|
||||
TpzCtx ctx;
|
||||
topazCryptoInit(&ctx, PID, 8); //is this length correct
|
||||
topazCryptoDecrypt(&ctx, in, out, len);
|
||||
}
|
||||
|
||||
//
|
||||
// Try to decrypt a dkey record (contains the book PID)
|
||||
//
|
||||
unsigned char *decryptDkeyRecord(unsigned char *data, int len, unsigned char *PID) {
|
||||
decryptRecord(data, len, data, PID);
|
||||
//fields = unpack("3sB8sB8s3s",record);
|
||||
|
||||
if (strncmp(data, "PID", 3) || strncmp(data + 21, "pid", 3)) {
|
||||
fprintf(stderr, "Didn't find PID magic numbers in record\n");
|
||||
return NULL;
|
||||
}
|
||||
else if (data[3] != 8 || data[12] != 8) {
|
||||
fprintf(stderr, "Record didn't contain correct length fields\n");
|
||||
return NULL;
|
||||
}
|
||||
else if (strncmp(data + 4, PID, 8)) {
|
||||
fprintf(stderr, "Record didn't contain PID\n");
|
||||
return NULL;
|
||||
}
|
||||
return data + 13;
|
||||
}
|
||||
|
||||
//
|
||||
// Decrypt all the book's dkey records (contain the book PID)
|
||||
//
|
||||
|
||||
unsigned char *decryptDkeyRecords(Payload *data, unsigned char *PID) {
|
||||
int nbKeyRecords = data->blob[0]; //is this encoded number?
|
||||
int i, idx;
|
||||
idx = 1;
|
||||
unsigned char *key = NULL;
|
||||
// records = []
|
||||
for (i = 0; i < nbKeyRecords && idx < data->len; i++) {
|
||||
int length = data->blob[idx++];
|
||||
key = decryptDkeyRecord(data->blob + idx, length, PID);
|
||||
if (key) break; //???
|
||||
idx += length;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
void bufEncodeInt(cbuf *b, int i) {
|
||||
unsigned char encoded[16];
|
||||
int len = encodeNumber(i, encoded);
|
||||
b_add_buf(b, encoded, len);
|
||||
}
|
||||
|
||||
void bufEncodeString(cbuf *b, char *s) {
|
||||
bufEncodeInt(b, strlen(s));
|
||||
b_add_str(b, s);
|
||||
}
|
||||
|
||||
void writeTopazOutputFile(TopazFile *t, FILE *out, cbuf *tpzHeaders,
|
||||
cbuf *tpzBody, int explode) {
|
||||
int i, numHdrs = 0;
|
||||
HeaderRecord *h;
|
||||
b_add_str(tpzHeaders, "TPZ0");
|
||||
for (h = t->hdrs; h; h = h->next) {
|
||||
if (strcmp(h->tag, "dkey")) {
|
||||
numHdrs++;
|
||||
}
|
||||
}
|
||||
bufEncodeInt(tpzHeaders, numHdrs);
|
||||
|
||||
b_add_byte(tpzBody, 0x40);
|
||||
|
||||
for (h = t->hdrs; h; h = h->next) {
|
||||
Record *r;
|
||||
int nr = 0, idx = 0;
|
||||
if (strcmp(h->tag, "dkey") == 0) continue;
|
||||
b_add_byte(tpzHeaders, 0x63);
|
||||
bufEncodeString(tpzHeaders, h->tag);
|
||||
for (r = h->rec; r; r = r->next) nr++;
|
||||
bufEncodeInt(tpzHeaders, nr);
|
||||
for (r = h->rec; r; r = r->next) {
|
||||
Payload *p;
|
||||
int b, e;
|
||||
bufEncodeInt(tpzHeaders, tpzBody->idx);
|
||||
bufEncodeString(tpzBody, h->tag);
|
||||
bufEncodeInt(tpzBody, idx);
|
||||
b = tpzBody->idx;
|
||||
p = getBookPayloadRecord(t, h->tag, idx++, explode);
|
||||
b_add_buf(tpzBody, p->blob, p->len);
|
||||
e = tpzBody->idx;
|
||||
|
||||
bufEncodeInt(tpzHeaders, r->length); //this is length of blob portion after decompression
|
||||
if (explode) {
|
||||
bufEncodeInt(tpzHeaders, 0); //this is the length in the file if compressed
|
||||
}
|
||||
else {
|
||||
bufEncodeInt(tpzHeaders, r->compressed); //this is the length in the file if compressed
|
||||
}
|
||||
|
||||
freePayload(p);
|
||||
}
|
||||
}
|
||||
|
||||
b_add_byte(tpzHeaders, 0x64);
|
||||
}
|
||||
|
||||
82
Kindle_Mobi_Tools/skindle-06/tpz.h
Normal file
82
Kindle_Mobi_Tools/skindle-06/tpz.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
Copyright 2010 BartSimpson aka skindle
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef __TPZ_H
|
||||
#define __TPZ_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include "skinutils.h"
|
||||
|
||||
typedef struct _TpzCtx {
|
||||
unsigned int v[2];
|
||||
} TpzCtx;
|
||||
|
||||
void topazCryptoInit(TpzCtx *ctx, unsigned char *key, int klen);
|
||||
void topazCryptoDecrypt(TpzCtx *ctx, unsigned char *in, unsigned char *out, int len);
|
||||
int bookReadEncodedNumber(FILE *f);
|
||||
int encodeNumber(int number, unsigned char *out);
|
||||
char *bookReadString(FILE *f);
|
||||
|
||||
typedef struct _Payload {
|
||||
unsigned char *blob;
|
||||
unsigned int len;
|
||||
char *name;
|
||||
int index;
|
||||
} Payload;
|
||||
|
||||
typedef struct _Record {
|
||||
int offset;
|
||||
int length;
|
||||
int compressed;
|
||||
struct _Record *next;
|
||||
} Record;
|
||||
|
||||
typedef struct _HeaderRecord {
|
||||
char *tag;
|
||||
Record *rec;
|
||||
struct _HeaderRecord *next;
|
||||
} HeaderRecord;
|
||||
|
||||
typedef struct _TopazFile {
|
||||
FILE *f;
|
||||
HeaderRecord *hdrs;
|
||||
unsigned char *bookKey;
|
||||
unsigned int bodyOffset;
|
||||
MapList *metadata;
|
||||
PidList *pids; //extra pids to try from command line
|
||||
} TopazFile;
|
||||
|
||||
Record *bookReadHeaderRecordData(FILE *f);
|
||||
void freeRecordList(Record *r);
|
||||
void freeHeaderList(HeaderRecord *r);
|
||||
void freeTopazFile(TopazFile *t);
|
||||
HeaderRecord *parseTopazHeaderRecord(FILE *f);
|
||||
HeaderRecord *addRecord(HeaderRecord *head, HeaderRecord *r);
|
||||
TopazFile *parseTopazHeader(FILE *f);
|
||||
void freeTopazFile(TopazFile *tpz);
|
||||
HeaderRecord *findHeader(TopazFile *tpz, char *tag);
|
||||
void freePayload(Payload *p);
|
||||
Payload *getBookPayloadRecord(TopazFile *t, char *name, int index, int explode);
|
||||
char *getMetadata(TopazFile *t, char *key);
|
||||
void parseMetadata(TopazFile *t);
|
||||
void decryptRecord(unsigned char *in, int len, unsigned char *out, unsigned char *PID);
|
||||
unsigned char *decryptDkeyRecord(unsigned char *data, int len, unsigned char *PID);
|
||||
unsigned char *decryptDkeyRecords(Payload *data, unsigned char *PID);
|
||||
void writeTopazOutputFile(TopazFile *t, FILE *out, cbuf *tpzHeaders,
|
||||
cbuf *tpzBody, int explode);
|
||||
|
||||
|
||||
#endif
|
||||
332
Kindle_Mobi_Tools/skindle-06/zconf.h
Normal file
332
Kindle_Mobi_Tools/skindle-06/zconf.h
Normal file
@@ -0,0 +1,332 @@
|
||||
/* zconf.h -- configuration of the zlib compression library
|
||||
* Copyright (C) 1995-2005 Jean-loup Gailly.
|
||||
* For conditions of distribution and use, see copyright notice in zlib.h
|
||||
*/
|
||||
|
||||
/* @(#) $Id$ */
|
||||
|
||||
#ifndef ZCONF_H
|
||||
#define ZCONF_H
|
||||
|
||||
/*
|
||||
* If you *really* need a unique prefix for all types and library functions,
|
||||
* compile with -DZ_PREFIX. The "standard" zlib should be compiled without it.
|
||||
*/
|
||||
#ifdef Z_PREFIX
|
||||
# define deflateInit_ z_deflateInit_
|
||||
# define deflate z_deflate
|
||||
# define deflateEnd z_deflateEnd
|
||||
# define inflateInit_ z_inflateInit_
|
||||
# define inflate z_inflate
|
||||
# define inflateEnd z_inflateEnd
|
||||
# define deflateInit2_ z_deflateInit2_
|
||||
# define deflateSetDictionary z_deflateSetDictionary
|
||||
# define deflateCopy z_deflateCopy
|
||||
# define deflateReset z_deflateReset
|
||||
# define deflateParams z_deflateParams
|
||||
# define deflateBound z_deflateBound
|
||||
# define deflatePrime z_deflatePrime
|
||||
# define inflateInit2_ z_inflateInit2_
|
||||
# define inflateSetDictionary z_inflateSetDictionary
|
||||
# define inflateSync z_inflateSync
|
||||
# define inflateSyncPoint z_inflateSyncPoint
|
||||
# define inflateCopy z_inflateCopy
|
||||
# define inflateReset z_inflateReset
|
||||
# define inflateBack z_inflateBack
|
||||
# define inflateBackEnd z_inflateBackEnd
|
||||
# define compress z_compress
|
||||
# define compress2 z_compress2
|
||||
# define compressBound z_compressBound
|
||||
# define uncompress z_uncompress
|
||||
# define adler32 z_adler32
|
||||
# define crc32 z_crc32
|
||||
# define get_crc_table z_get_crc_table
|
||||
# define zError z_zError
|
||||
|
||||
# define alloc_func z_alloc_func
|
||||
# define free_func z_free_func
|
||||
# define in_func z_in_func
|
||||
# define out_func z_out_func
|
||||
# define Byte z_Byte
|
||||
# define uInt z_uInt
|
||||
# define uLong z_uLong
|
||||
# define Bytef z_Bytef
|
||||
# define charf z_charf
|
||||
# define intf z_intf
|
||||
# define uIntf z_uIntf
|
||||
# define uLongf z_uLongf
|
||||
# define voidpf z_voidpf
|
||||
# define voidp z_voidp
|
||||
#endif
|
||||
|
||||
#if defined(__MSDOS__) && !defined(MSDOS)
|
||||
# define MSDOS
|
||||
#endif
|
||||
#if (defined(OS_2) || defined(__OS2__)) && !defined(OS2)
|
||||
# define OS2
|
||||
#endif
|
||||
#if defined(_WINDOWS) && !defined(WINDOWS)
|
||||
# define WINDOWS
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(_WIN32_WCE) || defined(__WIN32__)
|
||||
# ifndef WIN32
|
||||
# define WIN32
|
||||
# endif
|
||||
#endif
|
||||
#if (defined(MSDOS) || defined(OS2) || defined(WINDOWS)) && !defined(WIN32)
|
||||
# if !defined(__GNUC__) && !defined(__FLAT__) && !defined(__386__)
|
||||
# ifndef SYS16BIT
|
||||
# define SYS16BIT
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compile with -DMAXSEG_64K if the alloc function cannot allocate more
|
||||
* than 64k bytes at a time (needed on systems with 16-bit int).
|
||||
*/
|
||||
#ifdef SYS16BIT
|
||||
# define MAXSEG_64K
|
||||
#endif
|
||||
#ifdef MSDOS
|
||||
# define UNALIGNED_OK
|
||||
#endif
|
||||
|
||||
#ifdef __STDC_VERSION__
|
||||
# ifndef STDC
|
||||
# define STDC
|
||||
# endif
|
||||
# if __STDC_VERSION__ >= 199901L
|
||||
# ifndef STDC99
|
||||
# define STDC99
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(__STDC__) || defined(__cplusplus))
|
||||
# define STDC
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(__GNUC__) || defined(__BORLANDC__))
|
||||
# define STDC
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(MSDOS) || defined(WINDOWS) || defined(WIN32))
|
||||
# define STDC
|
||||
#endif
|
||||
#if !defined(STDC) && (defined(OS2) || defined(__HOS_AIX__))
|
||||
# define STDC
|
||||
#endif
|
||||
|
||||
#if defined(__OS400__) && !defined(STDC) /* iSeries (formerly AS/400). */
|
||||
# define STDC
|
||||
#endif
|
||||
|
||||
#ifndef STDC
|
||||
# ifndef const /* cannot use !defined(STDC) && !defined(const) on Mac */
|
||||
# define const /* note: need a more gentle solution here */
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Some Mac compilers merge all .h files incorrectly: */
|
||||
#if defined(__MWERKS__)||defined(applec)||defined(THINK_C)||defined(__SC__)
|
||||
# define NO_DUMMY_DECL
|
||||
#endif
|
||||
|
||||
/* Maximum value for memLevel in deflateInit2 */
|
||||
#ifndef MAX_MEM_LEVEL
|
||||
# ifdef MAXSEG_64K
|
||||
# define MAX_MEM_LEVEL 8
|
||||
# else
|
||||
# define MAX_MEM_LEVEL 9
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Maximum value for windowBits in deflateInit2 and inflateInit2.
|
||||
* WARNING: reducing MAX_WBITS makes minigzip unable to extract .gz files
|
||||
* created by gzip. (Files created by minigzip can still be extracted by
|
||||
* gzip.)
|
||||
*/
|
||||
#ifndef MAX_WBITS
|
||||
# define MAX_WBITS 15 /* 32K LZ77 window */
|
||||
#endif
|
||||
|
||||
/* The memory requirements for deflate are (in bytes):
|
||||
(1 << (windowBits+2)) + (1 << (memLevel+9))
|
||||
that is: 128K for windowBits=15 + 128K for memLevel = 8 (default values)
|
||||
plus a few kilobytes for small objects. For example, if you want to reduce
|
||||
the default memory requirements from 256K to 128K, compile with
|
||||
make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
|
||||
Of course this will generally degrade compression (there's no free lunch).
|
||||
|
||||
The memory requirements for inflate are (in bytes) 1 << windowBits
|
||||
that is, 32K for windowBits=15 (default value) plus a few kilobytes
|
||||
for small objects.
|
||||
*/
|
||||
|
||||
/* Type declarations */
|
||||
|
||||
#ifndef OF /* function prototypes */
|
||||
# ifdef STDC
|
||||
# define OF(args) args
|
||||
# else
|
||||
# define OF(args) ()
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* The following definitions for FAR are needed only for MSDOS mixed
|
||||
* model programming (small or medium model with some far allocations).
|
||||
* This was tested only with MSC; for other MSDOS compilers you may have
|
||||
* to define NO_MEMCPY in zutil.h. If you don't need the mixed model,
|
||||
* just define FAR to be empty.
|
||||
*/
|
||||
#ifdef SYS16BIT
|
||||
# if defined(M_I86SM) || defined(M_I86MM)
|
||||
/* MSC small or medium model */
|
||||
# define SMALL_MEDIUM
|
||||
# ifdef _MSC_VER
|
||||
# define FAR _far
|
||||
# else
|
||||
# define FAR far
|
||||
# endif
|
||||
# endif
|
||||
# if (defined(__SMALL__) || defined(__MEDIUM__))
|
||||
/* Turbo C small or medium model */
|
||||
# define SMALL_MEDIUM
|
||||
# ifdef __BORLANDC__
|
||||
# define FAR _far
|
||||
# else
|
||||
# define FAR far
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(WINDOWS) || defined(WIN32)
|
||||
/* If building or using zlib as a DLL, define ZLIB_DLL.
|
||||
* This is not mandatory, but it offers a little performance increase.
|
||||
*/
|
||||
# ifdef ZLIB_DLL
|
||||
# if defined(WIN32) && (!defined(__BORLANDC__) || (__BORLANDC__ >= 0x500))
|
||||
# ifdef ZLIB_INTERNAL
|
||||
# define ZEXTERN extern __declspec(dllexport)
|
||||
# else
|
||||
# define ZEXTERN extern __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
# endif /* ZLIB_DLL */
|
||||
/* If building or using zlib with the WINAPI/WINAPIV calling convention,
|
||||
* define ZLIB_WINAPI.
|
||||
* Caution: the standard ZLIB1.DLL is NOT compiled using ZLIB_WINAPI.
|
||||
*/
|
||||
# ifdef ZLIB_WINAPI
|
||||
# ifdef FAR
|
||||
# undef FAR
|
||||
# endif
|
||||
# include <windows.h>
|
||||
/* No need for _export, use ZLIB.DEF instead. */
|
||||
/* For complete Windows compatibility, use WINAPI, not __stdcall. */
|
||||
# define ZEXPORT WINAPI
|
||||
# ifdef WIN32
|
||||
# define ZEXPORTVA WINAPIV
|
||||
# else
|
||||
# define ZEXPORTVA FAR CDECL
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined (__BEOS__)
|
||||
# ifdef ZLIB_DLL
|
||||
# ifdef ZLIB_INTERNAL
|
||||
# define ZEXPORT __declspec(dllexport)
|
||||
# define ZEXPORTVA __declspec(dllexport)
|
||||
# else
|
||||
# define ZEXPORT __declspec(dllimport)
|
||||
# define ZEXPORTVA __declspec(dllimport)
|
||||
# endif
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef ZEXTERN
|
||||
# define ZEXTERN extern
|
||||
#endif
|
||||
#ifndef ZEXPORT
|
||||
# define ZEXPORT
|
||||
#endif
|
||||
#ifndef ZEXPORTVA
|
||||
# define ZEXPORTVA
|
||||
#endif
|
||||
|
||||
#ifndef FAR
|
||||
# define FAR
|
||||
#endif
|
||||
|
||||
#if !defined(__MACTYPES__)
|
||||
typedef unsigned char Byte; /* 8 bits */
|
||||
#endif
|
||||
typedef unsigned int uInt; /* 16 bits or more */
|
||||
typedef unsigned long uLong; /* 32 bits or more */
|
||||
|
||||
#ifdef SMALL_MEDIUM
|
||||
/* Borland C/C++ and some old MSC versions ignore FAR inside typedef */
|
||||
# define Bytef Byte FAR
|
||||
#else
|
||||
typedef Byte FAR Bytef;
|
||||
#endif
|
||||
typedef char FAR charf;
|
||||
typedef int FAR intf;
|
||||
typedef uInt FAR uIntf;
|
||||
typedef uLong FAR uLongf;
|
||||
|
||||
#ifdef STDC
|
||||
typedef void const *voidpc;
|
||||
typedef void FAR *voidpf;
|
||||
typedef void *voidp;
|
||||
#else
|
||||
typedef Byte const *voidpc;
|
||||
typedef Byte FAR *voidpf;
|
||||
typedef Byte *voidp;
|
||||
#endif
|
||||
|
||||
#if 0 /* HAVE_UNISTD_H -- this line is updated by ./configure */
|
||||
# include <sys/types.h> /* for off_t */
|
||||
# include <unistd.h> /* for SEEK_* and off_t */
|
||||
# ifdef VMS
|
||||
# include <unixio.h> /* for off_t */
|
||||
# endif
|
||||
# define z_off_t off_t
|
||||
#endif
|
||||
#ifndef SEEK_SET
|
||||
# define SEEK_SET 0 /* Seek from beginning of file. */
|
||||
# define SEEK_CUR 1 /* Seek from current position. */
|
||||
# define SEEK_END 2 /* Set file pointer to EOF plus "offset" */
|
||||
#endif
|
||||
#ifndef z_off_t
|
||||
# define z_off_t long
|
||||
#endif
|
||||
|
||||
#if defined(__OS400__)
|
||||
# define NO_vsnprintf
|
||||
#endif
|
||||
|
||||
#if defined(__MVS__)
|
||||
# define NO_vsnprintf
|
||||
# ifdef FAR
|
||||
# undef FAR
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* MVS linker does not support external names larger than 8 bytes */
|
||||
#if defined(__MVS__)
|
||||
# pragma map(deflateInit_,"DEIN")
|
||||
# pragma map(deflateInit2_,"DEIN2")
|
||||
# pragma map(deflateEnd,"DEEND")
|
||||
# pragma map(deflateBound,"DEBND")
|
||||
# pragma map(inflateInit_,"ININ")
|
||||
# pragma map(inflateInit2_,"ININ2")
|
||||
# pragma map(inflateEnd,"INEND")
|
||||
# pragma map(inflateSync,"INSY")
|
||||
# pragma map(inflateSetDictionary,"INSEDI")
|
||||
# pragma map(compressBound,"CMBND")
|
||||
# pragma map(inflate_table,"INTABL")
|
||||
# pragma map(inflate_fast,"INFA")
|
||||
# pragma map(inflate_copyright,"INCOPY")
|
||||
#endif
|
||||
|
||||
#endif /* ZCONF_H */
|
||||
1357
Kindle_Mobi_Tools/skindle-06/zlib.h
Normal file
1357
Kindle_Mobi_Tools/skindle-06/zlib.h
Normal file
File diff suppressed because it is too large
Load Diff
300
Kindle_Mobi_Tools/unswindle/mobidedrm.py
Normal file
300
Kindle_Mobi_Tools/unswindle/mobidedrm.py
Normal file
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# This is a python script. You need a Python interpreter to run it.
|
||||
# For example, ActiveState Python, which exists for windows.
|
||||
#
|
||||
# It can run standalone to convert files, or it can be installed as a
|
||||
# plugin for Calibre (http://calibre-ebook.com/about) so that
|
||||
# importing files with DRM 'Just Works'.
|
||||
#
|
||||
# To create a Calibre plugin, rename this file so that the filename
|
||||
# ends in '_plugin.py', put it into a ZIP file and import that Calibre
|
||||
# using its plugin configuration GUI.
|
||||
#
|
||||
# Changelog
|
||||
# 0.01 - Initial version
|
||||
# 0.02 - Huffdic compressed books were not properly decrypted
|
||||
# 0.03 - Wasn't checking MOBI header length
|
||||
# 0.04 - Wasn't sanity checking size of data record
|
||||
# 0.05 - It seems that the extra data flags take two bytes not four
|
||||
# 0.06 - And that low bit does mean something after all :-)
|
||||
# 0.07 - The extra data flags aren't present in MOBI header < 0xE8 in size
|
||||
# 0.08 - ...and also not in Mobi header version < 6
|
||||
# 0.09 - ...but they are there with Mobi header version 6, header size 0xE4!
|
||||
# 0.10 - Outputs unencrypted files as-is, so that when run as a Calibre
|
||||
# import filter it works when importing unencrypted files.
|
||||
# Also now handles encrypted files that don't need a specific PID.
|
||||
# 0.11 - use autoflushed stdout and proper return values
|
||||
# 0.12 - Fix for problems with metadata import as Calibre plugin, report errors
|
||||
# 0.13 - Formatting fixes: retabbed file, removed trailing whitespace
|
||||
# and extra blank lines, converted CR/LF pairs at ends of each line,
|
||||
# and other cosmetic fixes.
|
||||
# 0.14 - Working out when the extra data flags are present has been problematic
|
||||
# Versions 7 through 9 have tried to tweak the conditions, but have been
|
||||
# only partially successful. Closer examination of lots of sample
|
||||
# files reveals that a confusin has arisen because trailing data entries
|
||||
# are not encrypted, but it turns out that the multibyte entries
|
||||
# in utf8 file are encrypted. (Although neither kind gets compressed.)
|
||||
# This knowledge leads to a simplification of the test for the
|
||||
# trailing data byte flags - version 5 and higher AND header size >= 0xE4.
|
||||
|
||||
__version__ = '0.14'
|
||||
|
||||
import sys
|
||||
import struct
|
||||
import binascii
|
||||
|
||||
class Unbuffered:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
def write(self, data):
|
||||
self.stream.write(data)
|
||||
self.stream.flush()
|
||||
def __getattr__(self, attr):
|
||||
return getattr(self.stream, attr)
|
||||
|
||||
class DrmException(Exception):
|
||||
pass
|
||||
|
||||
# Implementation of Pukall Cipher 1
|
||||
def PC1(key, src, decryption=True):
|
||||
sum1 = 0;
|
||||
sum2 = 0;
|
||||
keyXorVal = 0;
|
||||
if len(key)!=16:
|
||||
print "Bad key length!"
|
||||
return None
|
||||
wkey = []
|
||||
for i in xrange(8):
|
||||
wkey.append(ord(key[i*2])<<8 | ord(key[i*2+1]))
|
||||
|
||||
dst = ""
|
||||
for i in xrange(len(src)):
|
||||
temp1 = 0;
|
||||
byteXorVal = 0;
|
||||
for j in xrange(8):
|
||||
temp1 ^= wkey[j]
|
||||
sum2 = (sum2+j)*20021 + sum1
|
||||
sum1 = (temp1*346)&0xFFFF
|
||||
sum2 = (sum2+sum1)&0xFFFF
|
||||
temp1 = (temp1*20021+1)&0xFFFF
|
||||
byteXorVal ^= temp1 ^ sum2
|
||||
curByte = ord(src[i])
|
||||
if not decryption:
|
||||
keyXorVal = curByte * 257;
|
||||
curByte = ((curByte ^ (byteXorVal >> 8)) ^ byteXorVal) & 0xFF
|
||||
if decryption:
|
||||
keyXorVal = curByte * 257;
|
||||
for j in xrange(8):
|
||||
wkey[j] ^= keyXorVal;
|
||||
dst+=chr(curByte)
|
||||
return dst
|
||||
|
||||
def checksumPid(s):
|
||||
letters = "ABCDEFGHIJKLMNPQRSTUVWXYZ123456789"
|
||||
crc = (~binascii.crc32(s,-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]
|
||||
crc >>= 8
|
||||
return res
|
||||
|
||||
def getSizeOfTrailingDataEntries(ptr, size, flags):
|
||||
def getSizeOfTrailingDataEntry(ptr, size):
|
||||
bitpos, result = 0, 0
|
||||
if size <= 0:
|
||||
return result
|
||||
while True:
|
||||
v = ord(ptr[size-1])
|
||||
result |= (v & 0x7F) << bitpos
|
||||
bitpos += 7
|
||||
size -= 1
|
||||
if (v & 0x80) != 0 or (bitpos >= 28) or (size == 0):
|
||||
return result
|
||||
num = 0
|
||||
testflags = flags >> 1
|
||||
while testflags:
|
||||
if testflags & 1:
|
||||
num += getSizeOfTrailingDataEntry(ptr, size - num)
|
||||
testflags >>= 1
|
||||
# Multibyte data, if present, is included in the encryption, so
|
||||
# we do not need to check the low bit.
|
||||
# if flags & 1:
|
||||
# num += (ord(ptr[size - num - 1]) & 0x3) + 1
|
||||
return num
|
||||
|
||||
class DrmStripper:
|
||||
def loadSection(self, section):
|
||||
if (section + 1 == self.num_sections):
|
||||
endoff = len(self.data_file)
|
||||
else:
|
||||
endoff = self.sections[section + 1][0]
|
||||
off = self.sections[section][0]
|
||||
return self.data_file[off:endoff]
|
||||
|
||||
def patch(self, off, new):
|
||||
self.data_file = self.data_file[:off] + new + self.data_file[off+len(new):]
|
||||
|
||||
def patchSection(self, section, new, in_off = 0):
|
||||
if (section + 1 == self.num_sections):
|
||||
endoff = len(self.data_file)
|
||||
else:
|
||||
endoff = self.sections[section + 1][0]
|
||||
off = self.sections[section][0]
|
||||
assert off + in_off + len(new) <= endoff
|
||||
self.patch(off + in_off, new)
|
||||
|
||||
def parseDRM(self, data, count, pid):
|
||||
pid = pid.ljust(16,'\0')
|
||||
keyvec1 = "\x72\x38\x33\xB0\xB4\xF2\xE3\xCA\xDF\x09\x01\xD6\xE2\xE0\x3F\x96"
|
||||
temp_key = PC1(keyvec1, pid, False)
|
||||
temp_key_sum = sum(map(ord,temp_key)) & 0xff
|
||||
found_key = None
|
||||
for i in xrange(count):
|
||||
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
|
||||
cookie = PC1(temp_key, cookie)
|
||||
ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
|
||||
if verification == ver and cksum == temp_key_sum and (flags & 0x1F) == 1:
|
||||
found_key = finalkey
|
||||
break
|
||||
if not found_key:
|
||||
# Then try the default encoding that doesn't require a PID
|
||||
temp_key = keyvec1
|
||||
temp_key_sum = sum(map(ord,temp_key)) & 0xff
|
||||
for i in xrange(count):
|
||||
verification, size, type, cksum, cookie = struct.unpack('>LLLBxxx32s', data[i*0x30:i*0x30+0x30])
|
||||
cookie = PC1(temp_key, cookie)
|
||||
ver,flags,finalkey,expiry,expiry2 = struct.unpack('>LL16sLL', cookie)
|
||||
if verification == ver and cksum == temp_key_sum:
|
||||
found_key = finalkey
|
||||
break
|
||||
return found_key
|
||||
|
||||
def __init__(self, data_file, pid):
|
||||
if checksumPid(pid[0:-2]) != pid:
|
||||
raise DrmException("invalid PID checksum")
|
||||
pid = pid[0:-2]
|
||||
|
||||
self.data_file = data_file
|
||||
header = data_file[0:72]
|
||||
if header[0x3C:0x3C+8] != 'BOOKMOBI':
|
||||
raise DrmException("invalid file format")
|
||||
self.num_sections, = struct.unpack('>H', data_file[76:78])
|
||||
|
||||
self.sections = []
|
||||
for i in xrange(self.num_sections):
|
||||
offset, a1,a2,a3,a4 = struct.unpack('>LBBBB', data_file[78+i*8:78+i*8+8])
|
||||
flags, val = a1, a2<<16|a3<<8|a4
|
||||
self.sections.append( (offset, flags, val) )
|
||||
|
||||
sect = self.loadSection(0)
|
||||
records, = struct.unpack('>H', sect[0x8:0x8+2])
|
||||
mobi_length, = struct.unpack('>L',sect[0x14:0x18])
|
||||
mobi_version, = struct.unpack('>L',sect[0x68:0x6C])
|
||||
extra_data_flags = 0
|
||||
print "MOBI header length = %d" %mobi_length
|
||||
print "MOBI header version = %d" %mobi_version
|
||||
if (mobi_length >= 0xE4) and (mobi_version >= 5):
|
||||
extra_data_flags, = struct.unpack('>H', sect[0xF2:0xF4])
|
||||
print "Extra Data Flags = %d" %extra_data_flags
|
||||
|
||||
crypto_type, = struct.unpack('>H', sect[0xC:0xC+2])
|
||||
if crypto_type == 0:
|
||||
print "This book is not encrypted."
|
||||
else:
|
||||
if crypto_type == 1:
|
||||
raise DrmException("cannot decode Mobipocket encryption type 1")
|
||||
if crypto_type != 2:
|
||||
raise DrmException("unknown encryption type: %d" % crypto_type)
|
||||
|
||||
# calculate the keys
|
||||
drm_ptr, drm_count, drm_size, drm_flags = struct.unpack('>LLLL', sect[0xA8:0xA8+16])
|
||||
if drm_count == 0:
|
||||
raise DrmException("no PIDs found in this file")
|
||||
found_key = self.parseDRM(sect[drm_ptr:drm_ptr+drm_size], drm_count, pid)
|
||||
if not found_key:
|
||||
raise DrmException("no key found. maybe the PID is incorrect")
|
||||
|
||||
# kill the drm keys
|
||||
self.patchSection(0, "\0" * drm_size, drm_ptr)
|
||||
# kill the drm pointers
|
||||
self.patchSection(0, "\xff" * 4 + "\0" * 12, 0xA8)
|
||||
# clear the crypto type
|
||||
self.patchSection(0, "\0" * 2, 0xC)
|
||||
|
||||
# decrypt sections
|
||||
print "Decrypting. Please wait...",
|
||||
for i in xrange(1, records+1):
|
||||
data = self.loadSection(i)
|
||||
extra_size = getSizeOfTrailingDataEntries(data, len(data), extra_data_flags)
|
||||
# print "record %d, extra_size %d" %(i,extra_size)
|
||||
self.patchSection(i, PC1(found_key, data[0:len(data) - extra_size]))
|
||||
print "done"
|
||||
|
||||
def getResult(self):
|
||||
return self.data_file
|
||||
|
||||
if not __name__ == "__main__":
|
||||
from calibre.customize import FileTypePlugin
|
||||
|
||||
class MobiDeDRM(FileTypePlugin):
|
||||
name = 'MobiDeDRM' # Name of the plugin
|
||||
description = 'Removes DRM from secure Mobi files'
|
||||
supported_platforms = ['linux', 'osx', 'windows'] # Platforms this plugin will run on
|
||||
author = 'The Dark Reverser' # The author of this plugin
|
||||
version = (0, 1, 4) # The version number of this plugin
|
||||
file_types = set(['prc','mobi','azw']) # The file types that this plugin will be applied to
|
||||
on_import = True # Run this plugin during the import
|
||||
|
||||
def run(self, path_to_ebook):
|
||||
from calibre.gui2 import is_ok_to_use_qt
|
||||
from PyQt4.Qt import QMessageBox
|
||||
PID = self.site_customization
|
||||
data_file = file(path_to_ebook, 'rb').read()
|
||||
ar = PID.split(',')
|
||||
for i in ar:
|
||||
try:
|
||||
unlocked_file = DrmStripper(data_file, i).getResult()
|
||||
except DrmException:
|
||||
# ignore the error
|
||||
pass
|
||||
else:
|
||||
of = self.temporary_file('.mobi')
|
||||
of.write(unlocked_file)
|
||||
of.close()
|
||||
return of.name
|
||||
if is_ok_to_use_qt():
|
||||
d = QMessageBox(QMessageBox.Warning, "MobiDeDRM Plugin", "Couldn't decode: %s\n\nImporting encrypted version." % path_to_ebook)
|
||||
d.show()
|
||||
d.raise_()
|
||||
d.exec_()
|
||||
return path_to_ebook
|
||||
|
||||
def customization_help(self, gui=False):
|
||||
return 'Enter PID (separate multiple PIDs with comma)'
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.stdout=Unbuffered(sys.stdout)
|
||||
print ('MobiDeDrm v%(__version__)s. '
|
||||
'Copyright 2008-2010 The Dark Reverser.' % globals())
|
||||
if len(sys.argv)<4:
|
||||
print "Removes protection from Mobipocket books"
|
||||
print "Usage:"
|
||||
print " %s <infile> <outfile> <PID>" % sys.argv[0]
|
||||
sys.exit(1)
|
||||
else:
|
||||
infile = sys.argv[1]
|
||||
outfile = sys.argv[2]
|
||||
pid = sys.argv[3]
|
||||
data_file = file(infile, 'rb').read()
|
||||
try:
|
||||
strippedFile = DrmStripper(data_file, pid)
|
||||
file(outfile, 'wb').write(strippedFile.getResult())
|
||||
except DrmException, e:
|
||||
print "Error: %s" % e
|
||||
sys.exit(1)
|
||||
sys.exit(0)
|
||||
Reference in New Issue
Block a user