LimHD:DVDCSS

From Lundman Wiki
Jump to: navigation, search

Unfortunately, libdvdcss's csstest program claim all the disks I've tried are not scrambled:


uclibc[/tmp]# ./csstest /dev/scsi/host0/bus0/target0/lun0/cd
cool, I found libdvdcss version 1.2.9
examples:
  ./csstest /dev/hdc 1024
  ./csstest D: 1024
  ./csstest scrambledfile.vob 1024
uclibc[/tmp]# ./csstest /dev/scsi/host0/bus0/target0/lun0/cd 1024 
cool, I found libdvdcss version 1.2.9
requested sector:      00     00     01     2u...     2{     1i     0�     2e     2�     17     1n     1}     2�     0d     05     1}     2�     09     1�     1�     2s     3g     2�     0l     03     2�...
sector is not scrambled

uclibc[/tmp]# ./csstest  /mnt/USB1/video_ts/vts_01_2.vob 1024
cool, I found libdvdcss version 1.2.9
zS@j>-�301�}�<O2NR���BV1�cn����-�pC>݃�"��?���?��E?��v?��?���?�̂?��E?���?��?���?@j>zS@j>-�30�...<O2NR���BV1�cn����-��?���?��E?��v?��?���?�̂?��E?���?��?���?��"Q
sector is not scrambled


Ah just a misunderstanding of how csstest works. That's the block I want to unscramble, and only video streams are scrambled.

uclibc[/tmp]# ./csstest /dev/scsi/host0/bus0/target0/lun0/cd 1000001
cool, I found libdvdcss version 1.2.9
requested sector:  00 00 01 2c... 0� 0� 2� 18 1� 2~ 2� 2� 0� 0} 1w 0� 2� 1p 0g 2                                                                     2� 0� 2e 2g 0� 0d 0�...
unscrambled sector:  00 00 01 2c... 2� 1� 1s 1z 1� 2 2� 1� 0� 1f 1u 0| 2p 0� 0�                                                                           1� 0� 0� 27 0� 13 1l...



So, I remain positive that this can be done. However, since Fuse does not want to run easily, I thought I would attempt to get CSS internal to em8xxx.o to work. All the code is in there, but curacao does not call it, or set it up.

The EM8xxx.PDF file claim to enable DVD-CSS you need but to load the title key with set_CSStitlekey() before demuxing begins.

I thought I would dlopen('em8xxx.o') from my program and call said function, but unfortunately you can not use dlopen in uClinux. (or at least not the version I have / LimHD runs).

So, the proper way is actually to open /dev/em8xxx0 device, and use ioctl to communicate with it. This is what curacao does after all. I have not done much ioctl programming lately, so I am rusty. This is the next thing I shall try however.

My pseudo-code for the daemon would be:

$cd_dev=/dev/scsi/host0/target0/lun0/cd
loop forver
  if $cd_dev has changed (how can we detect a new CD? research)
    call libdvdread and obtain disckey and titlekey
    call em8xxx.o via ioctl to hand over both.
    hope to hell that that just magically works.


Right, so the ioctl looks like:

#define EM8XXX_IOCTL_MAGIC 'E'

#define EM8XXX_IOCTL_SET_PROPERTY          _IOWR( EM8XXX_IOCTL_MAGIC,  1, struct em8xxx_property)

struct em8xxx_property {
       RMuint32 moduleId;
       RMuint32 propId;
       void *propInVal;
       RMuint32 propInSize;
       void *propOutVal;
       RMuint32 propOutSize;
       RMstatus status;
};

RMVideoDecoderPropertyID_CSSdisckey  = 6123 0x17eb
RMVideoDecoderPropertyID_CSStitlekey = 6124

I am not sure what ModuleId is of yet. It is used when the kernel code calls em8xxx library (private.o).

The documentation on the matter say:

The DVD decryption unit is controlled by 5 registers. The controller register indicates the 
number of bytes to process, typically 2048 for a DVD sector. If the count is programmed 
to 4095 (reset value), then the DVD decryption block is transparent. The start indicates 
how many bytes must be transferred before the decryption begins, typically 128 for a 
DVD sector. In a 2048-byte encrypted sector, the first 128-byte are clear and the last 1920- 
byte are encrypted. Before starting the decryption, the 40-bit title key must be pro- 
grammed in the key registers. The two state registers allow a backup of the decryption 
block internal state. This is required when a context switch occurs while a DVD sector is 
only partially processed.


I grabbed a disc and title key from a known disk, wrote a program to issue the ioctl to set the keys, setting moduleId to 0 (what should it be I wonder?) but unfortunately:

uclibc[/tmp]# ./2dvd_em8xxx /dev/em8xxx0 1
./2dvd_em8xxx: Patch Disc and Title keys into em8xxx.o
1' ...
set_CSStitlekey says 0

and then it hangs. Quite peculiar. ioctl actually returns, and successfully at that. And yet the machine dies before the program can exit.


Minor success. Using moduleId of 9 make it do "something". It loads both disckey and titlekey without crashing. Then when I try to play that vts_02_1.vob file it actually starts to play. First frame is perfect. However, play is very jerky (stop/start) and have a lot of macroblocks / corruption. I am used to the video being all "green" when it's encrypted so I am not sure what to think.

My current train of thought is that the DVDRom that I have will not let me read the data without first authenticating with the drive. With all my hacks, I must have eventually done so, which enabled LimHD to attempt to play the DVD. The jerks and corruption could be failure to descramble the blocks, but not all blocks are scrambled.

I am thinking I should call all the ewhlib functions to authenticate the drive now.

But ultimately, if a title changes key in the middle, we have no way to succeed.


Ok, what it comes down to is me authenticating the drive, which lets me play the files. They are not unscrambled so that is why they appear blocky. My loading of CSStitlekey is failing, 99% sure it is due to me not knowing what moduleId to use.

Looking at the assembler inside of em8xxx.o it seems clear that get_CSSchlgkey1 call is propId of 6177. Although, that could be wrong?

loc_15144
     LDR     R3, =0x1821
     CMP     R12, R3
     BEQ     loc_15314     ; getCSSchglkey1
     BHI     loc_15164     ; test for 0x1824

So, propId is 0x1812 or 6177.

This is all inside the EMhwlibGetDemuxTaskProperty function, which is listed as:

.data:0009985C EMhwlibVtable   DCD GetNotFound         ; 0
.data:0009985C                                         ; DATA XREF: EMhwlibSetProperty:off_4F20�o
.data:0009985C                                         ; EMhwlibGetProperty:off_4FBC�o ...
.data:0009985C                 DCD SetNotFound         ; 1
.data:0009985C                 DCD ExchangeNotFound    ; 2
.data:0009985C                 DCD SendNotFound        ; 3
.data:0009985C                 DCD ReceiveNotFound     ; 4

.data:0009985C                 DCD loc_28+2            ; 483
.data:0009985C                 DCD EMhwlibGetDemuxTaskProperty; 484
.data:0009985C                 DCD EMhwlibSetDemuxTaskProperty; 485
.data:0009985C                 DCD EMhwlibExchangeDemuxTaskProperty; 486

So, the 484th 4-byte entry in the EMhwlibVtable, or byte offset 1936.

The function that loads this up, is called EMhwlibGetProperty and the moduleId is stuffed into R1.

AND     R0, R1, #0xFF
MOV     R3, R1,LSR#8
AND     R6, R3, #0xFF
MOV     R8, R1,LSR#16
CMP     R0, #0x3A ; ':'     ; Maximum moduleId. So it should be between 0 and 0x3A.
BHI     loc_4FC0            ; Sets R0 to 0x12 and "returns", ie, failure.

LDR     R4, =EMhwlibVtable
ADD     R12, R0, R0,LSL#2
ADD     R12, R0, R12,LSL#1
MOV     R12, R12,LSL#2
[snip]
MOV     LR, PC
LDR     PC, [R4,R12]        ; Jump to EMhwlibVtable + value in R12. 
B       loc_4FC4            ; "returns".

By my calculations, using R1 with value 11, makes R12 have 484. However, does LDR take +484 (4 byte pointers) or, should it actually be 1936? If so, R1 needs to be 44. The example sources I have, which we know are already slightly different, has:

       DispGFXMultiScaler = 10,
       DispMainMixer = 12,
       DispVCRMixer = 13,

       DemuxEngine = 23,
       Demux = 24,
       DemuxProgram = 39,
       MpegEngine = 25,

       PLL = 43,
       DemuxTask = 44,
       DemuxOutput = 45,

Which makes me think that 44 is a better bet. Now it also uses bits 8-16 for index, and 24-16 as moduleTarget. No idea what they are. Many of the samples use 0 for index.

If I just call get_CSSchlgkey1 it just hangs. But perhaps I should call set_CSSkey1 first.

set_CSSkey1 is at: 0x1822 or 6178. It is in EMhwlibSetDemuxTaskProperty this time. This is at index 485 in EMhwlibVtable. However, EMhwlibSetDemuxTaskProperty loads the address of EMhwLibVtable + 4, which would call Set method instead of Get, with the same moduleId of 44.

The ioctl call is different. Set is 1, Get is 2.

Should we fiddle with the moduleIndex though? Using 1 crashes less than using 0.

Nearly all examples using module DemuxTask uses it like this:

RMuint32 demux_task = EMHWLIB_MODULE(DemuxTask, EMHWLIB_MODULE_INDEX(demux));

EMHWLIB_MODULE_INDEX(RMuint32 ModuleID) { return (ModuleID >> 8) & 0xff; }


Which it seems to get from:

       Tasks[i].Spi = FALSE;
       Tasks[i].SourceType = SourceType_dvd;
       Tasks[i].Id = i;
       Tasks[i].dcc_info = &dcc_info;
       err = RUACreateInstance(&pRUA, play_opt->chip_num);
       err = DCCOpen(pRUA, &pDCC);
       err = DCCInitMicroCodeEx(pDCC, DCCInitMode_InitDisplay); /*DCCInitMode_LeaveDisplay*/

       // open & connect Demux and DemuxProgramId.
       demux_profile.BitstreamFIFOSize = Tasks[i].Spi ? 0:DEMUX_FIFO_SIZE;
       demux_profile.XferFIFOCount = Tasks[i].Spi ? 0:DEMUX_XFER_FIFO_COUNT;
       demux_profile.DemuxID = Tasks[i].Id;
       demux_profile.DemuxProgram0 = i;
       demux_profile.DemuxProgram1 = 0xff;


       err = DCCOpenDemuxSource(pDCC, &demux_profile, &pDemuxSource);
       dcc_info.pDemuxSource = pDemuxSource;
 
       err = DCCGetDemuxSourceInfo(pDemuxSource, &(dcc_info.demux), &(dcc_info.demuxProgram0), &(dcc_info.demuxProgram1));


This I do not know about. So clearly there should be a specific value to be used. I'm hoping it is hard-coded/stable enough that I can use a static between firmwares, but it is also possible it is assigned dynamically it order of codecs loaded, or similar.