1 /* cssdec.c - simple css descrambling program using libdvdcss
2 * Copyright © 2012 Géraud Meyer <graud@gmx.com>
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 as
5 * published by the Free Software Foundation.
16 #include <dvdcss/dvdcss.h>
18 #define psz_progname "cssdec"
19 #ifndef DEFAULT_VERBOSITY
20 # define DEFAULT_VERBOSITY 1
22 char c_verbosity
= DEFAULT_VERBOSITY
;
23 #define READ_ERROR 1<<0
25 #define SCRAMBLED 1<<2
26 #define DECRYPTED 1<<3
27 #define KEY_CHANGED 1<<4
28 #define FAILED_DECRYPTION 1<<5
30 static int readsector ( dvdcss_t
, unsigned char *, const int );
31 static int isscrambled( const unsigned char * );
32 static int dumpsector ( unsigned char * );
33 static int printe ( const char, const char *, ... );
38 printf( "\t%s [-v] [-q] [-e] <file> [<start_sector> [<end_sector>]]\n",
42 int main( int argc
, char *argv
[] )
46 unsigned char p_data
[ DVDCSS_BLOCK_SIZE
* 2 ];
47 unsigned char *p_buffer
;
48 unsigned int i_sector
= 0, i_end
= INT_MAX
;
49 int i_keys
= 0, i_unread
= 0, i_scrambled
= 0, i_skipped
= 0,
50 i_undecrypted
= 0, i_unwritten
= 0;
56 while( (i_ret
= getopt( argc
, argv
, "qve" )) != -1 )
76 /* Command line args */
77 if( argc
< 1 || argc
> 3 )
79 printe( 1, "syntax error" );
83 psz_dvdfile
= argv
[0];
84 if( argc
>= 2 ) i_sector
= atoi( argv
[1] );
85 if( argc
>= 3 ) i_end
= atoi( argv
[2] );
87 /* Initialize libdvdcss */
88 printe( 2, "using libdvdcss version %s", dvdcss_interface_2
);
89 dvdcss
= dvdcss_open( psz_dvdfile
);
92 printe( 1, "opening of the DVD (%s) failed", psz_dvdfile
);
96 /* Align our read buffer */
97 p_buffer
= p_data
+ DVDCSS_BLOCK_SIZE
98 - ((long int)p_data
& (DVDCSS_BLOCK_SIZE
-1));
100 for( ; i_sector
< i_end
; i_sector
++ )
103 i_ret
= readsector( dvdcss
, p_buffer
, i_sector
);
106 if( i_ret
& READ_EOF
)
108 printe( 2, "stop reading before sector %d", i_sector
);
111 if( i_ret
& READ_ERROR
)
113 printe( 3, "sect %d: skipping processing", i_sector
);
117 if( i_ret
& SCRAMBLED
)
120 if( i_ret
& KEY_CHANGED
) i_keys
++;
121 if( ! (i_ret
& DECRYPTED
) )
123 if( i_ret
& KEY_CHANGED
)
130 /* Process the sector */
131 if( ! dumpsector( p_buffer
) ) {
132 printe( 1, "sect %d: writing failed; aborting", i_sector
);
138 /* Close the dvdcss device */
139 i_ret
= dvdcss_close( dvdcss
);
140 if( i_ret
< 0 ) printe( 1, "closing of the DVD failed" );
142 /* Summary & Return status */
143 printe( 2, "summary of processed sectors:" );
144 if( i_unwritten
) printe( 2, "error while writing" );
145 printe( 2, "%d unread sectors", i_unread
);
146 printe( 2, "%d undecrypted sectors", i_undecrypted
);
147 printe( 2, "%d key changes", i_keys
);
148 printe( 2, "%d (seemingly) scrambled sectors without a key", i_skipped
);
149 printe( 2, "%d scrambled sectors", i_scrambled
);
151 if( i_unread
|| i_unwritten
|| (b_noeof
&& i_sector
< i_end
) ) i_ret
|= 1<<4;
152 if( i_undecrypted
) i_ret
|= 1;
156 /* Read a sector; read decrypted again if it seems crypted;
157 * eventually try to get a key */
158 static int readsector( dvdcss_t dvdcss
, unsigned char *p_buffer
, const int i_sector
)
160 int i_ret
, i_return
= 0;
162 /* Seek at sector i_sector and read one sector */
163 i_ret
= dvdcss_seek( dvdcss
, i_sector
, DVDCSS_NOFLAGS
);
166 printe( 1, "sect %d: seek failed (%s)", i_sector
, dvdcss_error( dvdcss
) );
167 return i_return
| READ_ERROR
;
169 i_ret
= dvdcss_read( dvdcss
, p_buffer
, 1, DVDCSS_NOFLAGS
);
172 printe( 1, "sect %d: read failed (%s)", i_sector
, dvdcss_error( dvdcss
) );
173 return i_return
| READ_ERROR
;
177 printe( 1, "sect %d: EOF", i_sector
);
178 return i_return
| READ_EOF
;
181 if( ! isscrambled( p_buffer
) /* Check if sector is encrypted */ )
182 printe( 3, "sect %d: not crypted", i_sector
);
185 printe( 3, "sect %d: crypted", i_sector
);
186 i_return
|= SCRAMBLED
;
188 /* Seek at sector i_sector and try to decrypt sector */
189 i_ret
= dvdcss_seek( dvdcss
, i_sector
, DVDCSS_NOFLAGS
);
192 printe( 1, "sect %d: seek failed (%s)",
193 i_sector
, dvdcss_error( dvdcss
) );
196 i_ret
= dvdcss_read( dvdcss
, p_buffer
, 1, DVDCSS_READ_DECRYPT
);
197 /* Warning: A failure to decrypt is not considered an error in
198 * libdvdcss 1.2.12 */
201 printe( 2, "sect %d: read (decrypted) failed (%s)",
202 i_sector
, dvdcss_error( dvdcss
) );
206 if( isscrambled( p_buffer
) /* Check if sector is still encrypted */ )
208 /* Seek at sector i_sector and get a new key */
209 i_ret
= dvdcss_seek( dvdcss
, i_sector
, DVDCSS_SEEK_KEY
);
212 printe( 2, "sect %d: seek (key) failed (%s); skipping decryption",
213 i_sector
, dvdcss_error( dvdcss
) );
216 printe( 2, "sect %d: new key", i_sector
);
217 i_return
|= KEY_CHANGED
;
219 /* Re-try to decrypt sector */
220 i_ret
= dvdcss_read( dvdcss
, p_buffer
, 1, DVDCSS_READ_DECRYPT
);
223 printe( 2, "sect %d: read (decrypted) failed (%s); aborting decryption",
224 i_sector
, dvdcss_error( dvdcss
) );
229 if( isscrambled( p_buffer
) /* Check if the decryption really succeeded */ )
231 /* Probably a bug in libdvdcss not to have given an error earlier */
232 printe( 1, "sect %d: still apparently crypted after decryption",
234 i_return
|= FAILED_DECRYPTION
;
238 printe( 3, "sect %d: decrypted", i_sector
);
239 i_return
|= DECRYPTED
;
246 /* Check if a sector is scrambled */
247 static int isscrambled( const unsigned char *p_buffer
)
249 return p_buffer
[ 0x14 ] & 0x30;
252 /* Dump the sector on stdout */
253 static int dumpsector( unsigned char *p_buffer
)
256 n
= fwrite( (void *)p_buffer
, DVDCSS_BLOCK_SIZE
, 1, stdout
);
257 if( ferror( stdout
) )
259 printe( 1, "write error (%s)", strerror( errno
) );
266 /* Print a line on stderr preceded by the program name */
267 int printe( const char c_level
, const char *psz_format
, ... )
271 if( c_level
> c_verbosity
) return 1;
273 va_start( arg
, psz_format
);
274 fprintf( stderr
, "%s: ", psz_progname
);
275 i_ret
= vfprintf( stderr
, psz_format
, arg
);
277 fprintf( stderr
, "\n" );