skindle 6 with Topaz support

This commit is contained in:
Bart Simpson 2010-01-02 21:21:06 +00:00 committed by Apprentice Alf
parent 036eaa683f
commit 2c9852c5d4
15 changed files with 3915 additions and 941 deletions

View file

@ -1,21 +1,23 @@
OBJS=skindle.o md5.o sha1.o b64.o OBJS=skindle.o md5.o sha1.o b64.o skinutils.o cbuf.o mobi.o tpz.o
CC=gcc CC=gcc
LD=gcc LD=gcc
EXE=skindle EXE=skindle
EXTRALIBS=-lCrypt32 EXTRALIBS=libz.a -lCrypt32 -lAdvapi32
CFLAGS=-mno-cygwin
#use the following to strip your binary #use the following to strip your binary
LDFLAGS=-s LDFLAGS=-s -mno-cygwin
#LDFLAGS=-mno-cygwin
all: $(EXE) all: $(EXE)
%.o: %.c %.o: %.c
$(CC) -c $(CFLAGS) $(INC) $< -o $@ $(CC) -c $(CFLAGS) -g $(INC) $< -o $@
$(EXE): $(OBJS) $(EXE): $(OBJS)
$(LD) $(LDFLAGS) -o $@ $(OBJS) $(EXTRALIBS) $(LD) $(LDFLAGS) -o $@ -g $(OBJS) $(EXTRALIBS)
clean: clean:
-@rm *.o -@rm *.o

View file

@ -1,6 +1,6 @@
/* /*
Copyright 2010 BartSimpson Copyright 2010 BartSimpson aka skindle
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -16,13 +16,9 @@
*/ */
/* /*
* Dependencies: none * Dependencies: zlib (included)
* build on cygwin: gcc -o skindle skindle.c md5.c sha1.c b64.c -lCrypt32 * build on cygwin using make and the included make file
* Under cygwin, you can just type make to build it. * A fully functionaly windows executable is included
* While the binary builds if you use the -mno-cygwin switch, it fails to
* work for some reason. 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.
*/ */
/* /*
@ -31,6 +27,7 @@
* Requires your kindle.info file which can be found in something like: * Requires your kindle.info file which can be found in something like:
* <User home>\...\Amazon\Kindle For PC\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY} * <User home>\...\Amazon\Kindle For PC\{AMAwzsaPaaZAzmZzZQzgZCAkZ3AjA_AY}
* where ... varies by platform but is "Local Settings\Application Data" on XP * where ... varies by platform but is "Local Settings\Application Data" on XP
* skindle will attempt to find this file automatically.
*/ */
/* /*
@ -49,7 +46,9 @@
You guys shouldn't need to spend all your time responding to all the 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 changes Amazon is going to force you to make in unswindle each time
the release a new version. the release a new version.
CMBDTC - nice work on the topaz break!
Lawrence Lessig - You are my hero. 'Nuff said. 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 Thumbs down: Disney, MPAA, RIAA - you guys suck. Making billions off
of the exploitation of works out of copyright while vigourously of the exploitation of works out of copyright while vigourously
pushing copyright extension to prevent others from doing the same pushing copyright extension to prevent others from doing the same
@ -67,12 +66,20 @@ file and the data and algorthims that are used to derive per book
PID values. PID values.
Installing: Installing:
A cygwin compatable binary is included. You need a minimal cygwin A compiled binary is included. Though it was built using cygwin, it
installation in order to run it. To build from source, you will need should not require a cygwin installation in order to run it. To build
cygwin with gcc and make. This has not been tested with Visual Studio. 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: usage: ./skindle [-d] [-v] -i <ebook file> -o <output file> [-k kindle.info file] [-p pid]
./skindle <drm'ed prc file> <name of output file> <kindle.info path> -d optional, for topaz files only, produce a decompressed output file
You need to locate your kindle.info file somewhere on your system. -i required name of the input mobi or topaz file
You can copy it to a local directory, but you need to refer to it -o required name of the output file to generate
each time you run skindle. -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

85
skindle/cbuf.c Normal file
View 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
skindle/cbuf.h Normal file
View 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
skindle/libz.a Normal file

Binary file not shown.

365
skindle/mobi.c Normal file
View 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
skindle/mobi.h Normal file
View 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

File diff suppressed because it is too large Load diff

Binary file not shown.

539
skindle/skinutils.c Normal file
View 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\\", &regkey) != 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
skindle/skinutils.h Normal file
View 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
skindle/tpz.c Normal file
View 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
skindle/tpz.h Normal file
View 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
skindle/zconf.h Normal file
View 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
skindle/zlib.h Normal file

File diff suppressed because it is too large Load diff