Flag: Tornado! Hurricane!

 Forums >>  Target Specific - General  >>  Kindle for PC DRM

Topic created on: December 11, 2009 04:05 CST by labba .

Hi all,
my friend just showed me this application \"Kindle for PC\" - http://www.amazon.com/gp/feature.html/ref=kcp_pc_mkt_lnd?docId=1000426311
that it able to read files (.prc) that are DRM and encrypted.

now i have seen in this url:

that someone already found how to decrypt the DRM on a given PID of the device but for the \"Kindle for PC\" they didn\'t found yet what is the PID or where it is stored.

I\'m starting to reverse the application of Amazon to see if it is possible for a user to extract his PID so he can later decrypt the book files and use it on other softwer to read other then \"Kindle for PC\" Amazon application.
i\'m buying a sample book so i can start tracing/RE it :-)

i have compared so far 2 type of running one without DRM and the other with DRM i have found that the behavior that decides if to continue or to show an error message is in this sub:

.text:00414270 sub_414270      proc near               ; CODE XREF: sub_414240:loc_41425Bp
.text:00414270                                         ; sub_4197F0:loc_4199C1p ...
.text:00414270 var_30          = dword ptr -30h
.text:00414270 var_2C          = dword ptr -2Ch
.text:00414270 var_28          = dword ptr -28h
.text:00414270 var_24          = byte ptr -24h
.text:00414270 var_20          = byte ptr -20h
.text:00414270 var_C           = dword ptr -0Ch
.text:00414270 var_4           = dword ptr -4
.text:00414270                 push    ebp
.text:00414271                 mov     ebp, esp
.text:00414273                 and     esp, 0FFFFFFF8h
.text:00414276                 mov     eax, large fs:0
.text:0041427C                 push    0FFFFFFFFh
.text:0041427E                 push    offset sub_A01580
.text:00414283                 push    eax
.text:00414284                 mov     large fs:0, esp
.text:0041428B                 sub     esp, 28h
.text:0041428E                 push    ebx
.text:0041428F                 push    esi
.text:00414290                 push    edi
.text:00414291                 mov     edi, ecx
.text:00414293                 mov     ecx, [edi+3Ch]
.text:00414296                 xor     ebx, ebx
.text:00414298                 cmp     ecx, ebx
.text:0041429A                 jz      short loc_4142A4
.text:0041429C                 mov     eax, [ecx]
.text:0041429E                 mov     edx, [eax+2Ch]
.text:004142A1                 push    ebx
.text:004142A2                 call    edx
.text:004142A4 loc_4142A4:                             ; CODE XREF: sub_414270+2Aj
.text:004142A4                 mov     eax, dword_D1CB60
.text:004142A9                 mov     [esp+40h+var_30], eax
.text:004142AD                 mov     ecx, 1
.text:004142B2                 lock xadd [eax], ecx
.text:004142B6                 mov     [esp+40h+var_4], ebx
.text:004142BA                 mov     eax, [edi+20h]
.text:004142BD                 cmp     eax, ebx
.text:004142BF                 jz      loc_41443B
.text:004142C5                 cmp     [eax+1Dh], bl
.text:004142C8                 jnz     loc_41443B
.text:004142CE                 cmp     [eax+14h], ebx
.text:004142D1                 jz      loc_4143C4
.text:004142D7                 lea     esi, [esp+40h+var_20]
.text:004142DB                 call    sub_46BFF0
.text:004142E0                 mov     byte ptr [esp+40h+var_4], 1
.text:004142E5                 mov     edx, [edi+20h]
.text:004142E8                 mov     ecx, [edx+14h]
.text:004142EB                 mov     eax, [ecx]
.text:004142ED                 mov     eax, [eax+8]
.text:004142F0                 mov     edx, esi
.text:004142F2                 push    edx
.text:004142F3                 lea     edx, [esp+44h+var_2C]
.text:004142F7                 push    edx
.text:004142F8                 call    eax
.text:004142FA                 lea     ecx, [esp+40h+var_28]
.text:004142FE                 push    ecx
.text:004142FF                 mov     byte ptr [esp+44h+var_4], 2
.text:00414304                 call    sub_43EC40
.text:00414309                 add     esp, 4
.text:0041430C                 push    eax
.text:0041430D                 lea     ecx, [esp+44h+var_30]
.text:00414311                 mov     byte ptr [esp+44h+var_4], 3
.text:00414316                 call    sub_904EE0
.text:0041431B                 mov     byte ptr [esp+40h+var_4], 2
.text:00414320                 mov     edx, [esp+40h+var_28]
.text:00414324                 or      eax, 0FFFFFFFFh
.text:00414327                 lock xadd [edx], eax
.text:0041432B                 jnz     short loc_41433A
.text:0041432D                 mov     ecx, [esp+40h+var_28]
.text:00414331                 push    ecx             ; void *
.text:00414332                 call    j_free
.text:00414337                 add     esp, 4
.text:0041433A loc_41433A:                             ; CODE XREF: sub_414270+BBj
.text:0041433A                 mov     ecx, [esp+40h+var_2C]
.text:0041433E                 cmp     ecx, ebx
.text:00414340                 jz      short loc_4143B7
.text:00414342                 push    1
.text:00414344                 push    ecx
.text:00414345                 mov     eax, esp
.text:00414347                 mov     [eax], ecx
.text:00414349                 mov     ecx, [esp+48h+var_2C]
.text:0041434D                 mov     [esp+48h+var_28], esp
.text:00414351                 cmp     ecx, ebx
.text:00414353                 jz      short loc_41435B
.text:00414355                 mov     edx, [ecx]
.text:00414357                 mov     eax, [edx]
.text:00414359                 call    eax
.text:0041435B loc_41435B:                             ; CODE XREF: sub_414270+E3j
.text:0041435B                 mov     byte ptr [esp+48h+var_4], 4
.text:00414360                 mov     ecx, [edi+20h]
.text:00414363                 mov     eax, [ecx+14h]
.text:00414366                 push    eax
.text:00414367                 call    sub_402AD0
.text:0041436C                 push    eax
.text:0041436D                 mov     byte ptr [esp+50h+var_4], 2
.text:00414372                 call    sub_403FA0
.text:00414377                 mov     byte ptr [esp+40h+var_4], 1
.text:0041437C                 mov     ecx, [esp+40h+var_2C]
.text:00414380                 cmp     ecx, ebx
.text:00414382                 jz      short loc_41438B
.text:00414384                 mov     edx, [ecx]
.text:00414386                 mov     eax, [edx+4]
.text:00414389                 call    eax
.text:0041438B loc_41438B:                             ; CODE XREF: sub_414270+112j
.text:0041438B                 lea     edi, [esp+40h+var_20]
.text:0041438F                 call    sub_403F60
.text:00414394                 mov     [esp+40h+var_4], 0FFFFFFFFh
.text:0041439C                 mov     ecx, [esp+40h+var_30]
.text:004143A0                 or      edx, 0FFFFFFFFh
.text:004143A3                 lock xadd [ecx], edx
.text:004143A7                 jnz     loc_41445D
.text:004143AD                 mov     eax, [esp+40h+var_30]
.text:004143B1                 push    eax
.text:004143B2                 jmp     loc_414455
.text:004143B7 ; ---------------------------------------------------------------------------
.text:004143B7 loc_4143B7:                             ; CODE XREF: sub_414270+D0j
.text:004143B7                 lea     edi, [esp+40h+var_20]
.text:004143BB                 mov     byte ptr [esp+40h+var_4], bl
.text:004143BF                 call    sub_403F60
.text:004143C4 loc_4143C4:                             ; CODE XREF: sub_414270+61j
.text:004143C4                 lea     ecx, [esp+40h+var_28]
.text:004143C8                 push    ecx
.text:004143C9                 call    sub_401500
.text:004143CE                 add     esp, 4
.text:004143D1                 push    offset aCouldNotOpenBo ; \"Could not open book, shoot!\"
.text:004143D6                 mov     ecx, eax
.text:004143D8                 mov     byte ptr [esp+44h+var_4], 5
.text:004143DD                 call    sub_401410
.text:004143E2                 lea     ecx, [esp+40h+var_28]
.text:004143E6                 mov     byte ptr [esp+40h+var_4], bl
.text:004143EA                 call    sub_401340
.text:004143EF                 mov     edx, dword_D1FD6C
.text:004143F5                 push    ecx             ; void *
.text:004143F6                 mov     eax, esp
.text:004143F8                 mov     [eax], edx
.text:004143FA                 mov     [esp+44h+var_2C], esp
.text:004143FE                 mov     eax, edx
.text:00414400                 mov     ecx, 1
.text:00414405                 lock xadd [eax], ecx
.text:00414409                 mov     edx, [esp+44h+var_30]
.text:0041440D                 push    ecx             ; void *
.text:0041440E                 mov     eax, esp
.text:00414410                 mov     [eax], edx
.text:00414412                 mov     eax, [esp+48h+var_30]
.text:00414416                 mov     dword ptr [esp+48h+var_24], esp
.text:0041441A                 mov     ecx, 1
.text:0041441F                 lock xadd [eax], ecx
.text:00414423                 mov     byte ptr [esp+48h+var_4], 7
.text:00414428                 call    sub_408980
.text:0041442D                 mov     eax, [eax+40h]
.text:00414430                 mov     edi, eax
.text:00414432                 mov     byte ptr [esp+48h+var_4], bl
.text:00414436                 call    BadBoy
.text:0041443B loc_41443B:                             ; CODE XREF: sub_414270+4Fj
.text:0041443B                                         ; sub_414270+58j
.text:0041443B                 mov     [esp+40h+var_4], 0FFFFFFFFh
.text:00414443                 mov     edx, [esp+40h+var_30]
.text:00414447                 or      eax, 0FFFFFFFFh
.text:0041444A                 lock xadd [edx], eax
.text:0041444E                 jnz     short loc_41445D
.text:00414450                 mov     ecx, [esp+40h+var_30]
.text:00414454                 push    ecx             ; void *
.text:00414455 loc_414455:                             ; CODE XREF: sub_414270+142j
.text:00414455                 call    j_free
.text:0041445A                 add     esp, 4
.text:0041445D loc_41445D:                             ; CODE XREF: sub_414270+137j
.text:0041445D                                         ; sub_414270+1DEj
.text:0041445D                 mov     ecx, [esp+40h+var_C]
.text:00414461                 pop     edi
.text:00414462                 pop     esi
.text:00414463                 mov     large fs:0, ecx
.text:0041446A                 pop     ebx
.text:0041446B                 mov     esp, ebp
.text:0041446D                 pop     ebp
.text:0041446E                 retn

the critical point of good and bad jump is in this place
00414340 JE SHORT

if all is good the the jump shouldn\'t be taken if its bad then it is taken
and then we will get to the private error string:
\"Could not open book, shoot!\"

i have looked a littel on the source code of : MobiDeDRM
and from what i have seen there is a vector like this:

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

i looked this bytes in the Kindle for PC:

and yes i found it!

005709AA                 mov     ecx, ds:dword_BFC5A8 ; keyvec1 like in - MobiDeDRM
.text:005709B0                 mov     edx, ds:dword_BFC5AC
.text:005709B6                 mov     eax, ds:dword_BFC5B0
.text:005709BB                 mov     [esp+0C4h+var_AC], ecx
.text:005709BF                 mov     ecx, ds:dword_BFC5B4
.text:005709C5                 mov     [esp+0C4h+var_98], 3
.text:005709CD                 mov     [esp+0C4h+var_A8], edx
.text:005709D1                 mov     [esp+0C4h+var_A4], eax
.text:005709D5                 mov     [esp+0C4h+var_A0], ecx
.text:005709D9                 jz      loc_570ABC
.text:005709DF                 mov     dword ptr [ebp+4], 1
.text:005709E6                 jmp     loc_570ABC

currently i\'m starting to debug to see when it is used...

i need a littel help with reverse this sub to fidn the PID
if anyone could help that would be nice.. i\'m currently working on this..


  BartSimpson   December 13, 2009 02:32.17 CST
Try hooking sub_54F7E0, then dumping the buffer pointed to by arg_0.  You will get 8 PID characters, you need to add the two checksum chars yourself.  See the checksumPid function in the mobidedrm scripts you referenced.

  labba   December 13, 2009 04:03.41 CST
i did that and i got 5 times BP and each time i get different string..
Your are saying that the PID can be one of those 5 or all 5 and i can just fix them by adding 2 chars to make from them valid (because of checksum) ?

  BartSimpson   December 13, 2009 13:16.36 CST
I hit BP 4 times and the PID that worked was the last one.  I don't know if other values from first three BP do work.  Adding 2 checksum chars to found pid allowed mobidedrm.py to work.  mobidedrm005.py would not works, it seems needing to many checksums

  labba   December 14, 2009 00:05.19 CST
when you say that it work you mean that you where able to decrypt a PRC file ? or that the application just work until the checksum sub ?

  BartSimpson   December 14, 2009 02:22.00 CST
The PID worked for decrypting a PRC file

  labba   December 14, 2009 03:19.01 CST
ok so i just send the last PID (less 2 last chars) to the function :

def checksumPid(s):
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

and the rerurn value will return me the good PID with needed 2 fixed chars.
i will let you know how it go..


  damo     December 18, 2009 06:56.14 CST
Maybe i have't konw what's your meaning.

  labba   December 19, 2009 19:20.32 CST
> damo: Maybe i have\'t konw what\'s your meaning.
never mind .. i figure it out.. and someone already posted a tool that does it all for ya:


  BartSimpson   December 29, 2009 02:47.27 CST
In an effort to contribute to the state of knowledge on KindleForPC, I just posted an all in one tool I am calling skindle to rapidshare:


This tool reads your kindle.info file and an input Mobi DRM'ed file to produce a dedrm'ed file. Thre is no dependency on mobidedrm and no use of the windows debugging api. This tool does not need to run KindleForPC in order to work. This tool derives the book specific PID in the same manner that K4PC does, then uses that PID in the same way that mobidedrm does to produce an output file.

C source and a compiled binary are included.

EDIT: 01/02/10 1903GMT updated link to new version

  CMBDTC   December 29, 2009 16:04.20 CST
I wanted to learn Python during the holidays, so I wrote a little script. It might not be very useful, but it is my first script, so don't be too harsh :


Happy New Year !

  BartSimpson   December 30, 2009 04:43.35 CST
you have a bug in your script at line 300 it should read
   nbValues = bookReadEncodedNumber()

Also, as written, you dump single decrypted records, do you intend to update this to dump the entire decrypted file?

  CMBDTC   December 30, 2009 06:43.39 CST
Here is a new version.


It should fix the problem with "invalid header" that people have, thanks to BartSimpson's fix. (I think there was the same problem on line 330).

It also tries to decrypt the book key with the Device PID as well as other PIDs that can be entered on the command line.

Well, it would be possible to rewrite a book with all the records decrypted, but as far as I can tell, the application still expects to have correct key and dkey data.

  Smabbage   December 30, 2009 10:30.02 CST
I've been able to identify the .prc file containing the topaz formatted book but need some detailed instructions on how to make this script work.  I've opened a CMD window in Windows 7, run the script but haven't been able to figure out the correct syntax to make it dump a decrypted book.  Is it ./script.py -o "output file" "input file"  ?  ./script.py -o "input file" "output file"  ?  I'm a bit confused as nothing seems to dump a decrypted book.  It acts like it runs for a second or two and then drops back to prompt.  Any help would be appreciated.  Thanks.

  chorpler     December 30, 2009 11:34.29 CST
Yeah, it no longer gives the error, but how do we know what records to output?  Does this not actually decode the Topaz format, just the encryption?

  chorpler     December 30, 2009 11:36.54 CST
OK, by doing the following:

cmbdtc11.py -r img:0 -o img1.jpg ebook.prc

I was able to get a working image file of the book cover out.  So that's one interesting thing...

  BartSimpson   December 30, 2009 12:18.05 CST
> Well, it would be possible to rewrite a book with all the records decrypted, but as far as I can tell, the application still expects to have correct key and dkey data.

If I understand your script correctly, the only thing that indicates a section is encoded is the recordIndex extracted in getBookPayloadRecord.  When you rewrite the decrypted sections are you flipping the sign of recordIndex as well?

  CMBDTC   December 30, 2009 13:58.31 CST
Here is version 2.0 :


It can output decrypted versions of books, since apparently for many people, a proof of concept that totally exposes the Topaz DRM is worth nothing. A few special cases have not been implemented, but I have never seen them in the books I own.

There is a catch though. The script has a little typo and won't decrypt books as is.

I would also strongly advise against using it script for piracy.

@Smabbage Have you noticed this is a reverse engineering forum, not a helpline for morons who want to be spoon fed one-click scripts that do all the work?

  chorpler     December 30, 2009 14:11.07 CST
Dude, you ROCK.  I can't believe this is finally cracked!  I want to buy you a beer.

  chorpler     December 30, 2009 15:52.36 CST
Although I see why you're so concerned about people using it for piracy: since it outputs an un-DRMed Topaz file, only Kindles or Amazon's Kindle apps can read it, and since the Kindle/Kindle apps can already read legally-acquired Topaz files, right now the only thing the average user can do with it is share their formerly-DRM-locked Topaz files with others ... in other words, spread pirated books.

But hopefully this will let us understand the structure of the Topaz file, and then it'll be useful for doing what most of us really want: converting Topaz to normal ePub, HTML, or other files so we can read ALL of our Kindle books on other devices.  I like to read them on my Windows Mobile phone, for instance, and until Amazon comes out with a Kindle for Windows Mobile app, I'm only able to read Mobipocket files on there.

  Smabbage   December 30, 2009 18:11.05 CST
It's always refreshing for a end user to be referred to as a Moron.  Thanks for that. :)  I wish you success in your efforts.

  BartSimpson   December 31, 2009 14:57.34 CST
CMBDTC, you may want to look at the length of your output records, you appear to be writing too much data.  After further study, I think that the actual field length is the third value in the header 3 tuples which represents the compressed length if the record is compressed.  I think the second value in each tuple is the uncompressed field length

  CMBDTC   January 1, 2010 06:15.45 CST
Here is a new version that should fix the problem of record length:


Thanks to all who have brought this issue to my attention.

This will probably be the last version of CMBDTC I release.

  grokker   January 2, 2010 07:00.03 CST
I'd like to modify CMBDTC, replacing the windll.crypt functions with something that would work cross-platform.  I'm guessing that PyCrypto has something that would replace CryptUnprotectData().  Can anyone point me in the right direction?

  BartSimpson   January 2, 2010 15:44.24 CST
MS claims that CryptProtect/UnprotectData tie an encrypted blob to a specific user and machine.  Not sure you can make this cross-platform unless there is a machine independent break for these functions

  bamaboy1217     January 20, 2010 13:41.46 CST
The unencrypt worked for sure, interestingly my topaz book was a .prc file?  I was under the (obviously wrong) assumption that topaz either came in azw1 via whisper or .tpz via the internet download.  I downloaded it via whisper for kindle for PC so perhaps that was different, but unswindle would not do it and CMBDTC's stuff did decrypt it.  Still cant read it on anything else obviously since its topaz, but that just struck me as odd.

  clarknova   January 28, 2010 15:59.56 CST
There is a really tiny bug in CMBDTC's encodeNumber function that will accidentally encode some numbers as negatives (16256-16383, 2080768-2097151, etc...).
I've made a quick fix to CMBDTC here: http://pastie.org/799495

Bart/skindle, the same for skindle -- it should just be a matter of adding a check after the do/while loop in the encodeNumber function in tpz.c to pad the output with an 0x80:

if ((*(b-1) == 0xFF) && (! neg)) *b++ = 0x80;

Or something... I dunno, my C is rusty.

Note: Registration is required to post to the forums.

There are 31,123 total registered users.

Recently Created Topics
Information on the t...
Information on the m...
Order Finax, Fincar ...
Information on the m...
Order Proscar (Finas...
Order Proscar, Finax...
Order Finasteride, F...
How to view IDA Pro'...
reverse MC9S12DG128
Looking for an advan...

Recent Forum Posts
Looking for an advan...
Looking for an advan...
Looking for an advan...
Identify RVA data in...
let 'IDAPython' impo...
How to find specific...
Problem with ollydbg
How can I write olly...
New LoadMAP plugin v...
Intel pin in loaded ...

Recent Blog Entries
Android Application Reversing

Breaking IonCUBE VM

Anatomy of a code tracer

IAT Patcher - new tool for ...

CryptoShark: code tracer ba...

More ...

Recent Blog Comments
ComPuer on:
Android Application Reversing

nieo on:
IAT Patcher - new tool for ...

djnemo on:
Kernel debugger vs user mod...

acel on:
Kernel debugger vs user mod...

pedram on:
frida.github.io: scriptable...

More ...

SoySauce Blueprint
Jun 6, 2008

[+] expand

View Gallery (11) / Submit