View Full Version : first attempt at cmd line iso decrypter based on libcdio/udf and dvdcss
spotter
18th October 2010, 04:10
So, I've had an issue with recovering DVDs.
I've written about this before, my methodology in linux is
1) lsdvd - to authenticate the disk
2) ddrescue to recover all of the DVD (sometimes requires getting a second DVD from the library, also possibly scratched)
this results in an image with CSS scrambled sectors.
this image seems playable in anything using libdvdread/nav/dvdcss (i.e. anything in Linux, and VLC on windows) if I pass the ISO to it.
However, that's not so good. I can remove the scrambled sectors if I mount this iso w/ daemon tools in windows and use dvd decrypter, though it complains a bit. so I decided it be nice to try and write a program in Linux that could do it, and what I have below is my 3rd attempt (my first attempt worked beautifully, except didn't know when to have dvdcss try to see if key needed to change, need to do it at the start of each VOB it seems, and my second attempt at just reading 1 2k block and calling dvdcss_seek() after each block was a horrible idea due to the way dvdcss works)
So what's the issue, the below code, is my attempt at making it use libcdio/udf to find the starting block for each VOB file. Unfortunately, libcdio/udf hide that information from the programmer. so a bit stuck. A hackish idea was to mount loopback mount the image and use the FIBMAP ioctl to find the blocks, but that requires root, which I don't want to do).
any good ideas on how to get this block information?
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/udf.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <ctype.h>
using namespace std;
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
vector<int> start_blocks;
#define MAX 1000
static void add_block(const udf_dirent_t *p_udf_dirent)
{
char *psz_fname=(char *) udf_get_filename(p_udf_dirent);
int len = strlen(psz_fname);
for(int i=0; i < len; i++) {
psz_fname[i] = tolower(psz_fname[i]);
}
if (! strcmp(psz_fname + (len-3), "vob")) {
printf("matched on name %s\n", psz_fname);
//TODO: start_blocks.push_back(p_udf_dirent->i_loc);
} else {
printf("didn't match on name %s\n", psz_fname);
}
}
void find_start_blocks(udf_t *p_udf, udf_dirent_t *p_udf_dirent)
{
if (!p_udf_dirent)
return;
while(udf_readdir(p_udf_dirent)) {
if (udf_is_dir(p_udf_dirent)) {
udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent) {
find_start_blocks(p_udf, p_udf_dirent2);
}
} else {
add_block(p_udf_dirent);
}
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
struct stat stat_buf;
int total_blocks;
int pos = 0;
udf_t *p_udf;
udf_dirent_t *p_udf_root;
char buffer[MAX*DVDCSS_BLOCK_SIZE];
if (argc != 3) {
printf("usage:\n\t %s <input iso> <output iso>\n", argv[0]);
return 1;
}
/* figure out how big the ISO image is */
if (stat(argv[1], &stat_buf) < 0) {
perror("failed to stat input iso");
return 1;
}
total_blocks = stat_buf.st_blocks / 4;
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_udf = udf_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
if (!(p_udf_root = udf_get_root(p_udf, true, 0))) {
fprintf(stderr, "couldn't find / in %s\n", argv[1]);
return 1;
}
find_start_blocks(p_udf, p_udf_root);
sort(start_blocks.begin(), start_blocks.end());
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
/* prep output file */
if ((fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) {
printf("failed to open output file\n");
return 1;
}
/* if not scrambled skip! */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
return 0;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int end = start_blocks[0];
start_blocks.erase(start_blocks.begin());
// don't dvdcss_seek first time around or last element (last block of dvd
if (pos && !start_blocks.empty()) {
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %d: %s\n", pos, dvdcss_error(input));
}
}
while (pos <= end) {
if (pos + len > end) {
len = end - pos;
}
if ((blocks_read = dvdcss_read(input, buffer, len, DVDCSS_READ_DECRYPT)) != len) {
fprintf(stderr, "didn't read %d blocks! read %d blocks\n", len, blocks_read);
}
pos += blocks_read;
if (write(fd, buffer, 2048 * blocks_read) != 2048 * blocks_read) {
fprintf(stderr, "write didn't write enough\n");
return 0;
}
}
}
}
spotter
18th October 2010, 19:16
so, here's some progress. added this function to libcdio's libudf, including an appropriate prototype to the exported headers
uint32_t udf_get_start_block(const udf_dirent_t *p_udf_dirent)
{
udf_t *p_udf = p_udf_dirent->p_udf;
uint32_t i_max_size;
lba_t i_start = offset_to_lba(p_udf_dirent, p_udf->i_position, &i_start,
&i_max_size);
return i_start;
}
code is then changed to what I include below, seems to work mostly, but returning an iso a bit difference size, need to investigate why. should be same size.
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/udf.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <ctype.h>
using namespace std;
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
vector<int> start_blocks;
#define MAX 1000
static void add_block(const udf_dirent_t *p_udf_dirent)
{
char *psz_fname=(char *) udf_get_filename(p_udf_dirent);
int len = strlen(psz_fname);
unsigned int start;
for(int i=0; i < len; i++) {
psz_fname[i] = tolower(psz_fname[i]);
}
if (! strcmp(psz_fname + (len-3), "vob")) {
start = udf_get_start_block(p_udf_dirent);
start_blocks.push_back(start);
printf("start block for %s = %u\n", psz_fname, start);
} else {
printf("didn't match on name %s\n", psz_fname);
}
}
void find_start_blocks(udf_t *p_udf, udf_dirent_t *p_udf_dirent)
{
if (!p_udf_dirent)
return;
while(udf_readdir(p_udf_dirent)) {
if (udf_is_dir(p_udf_dirent)) {
udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent) {
find_start_blocks(p_udf, p_udf_dirent2);
}
} else {
add_block(p_udf_dirent);
}
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
struct stat stat_buf;
int total_blocks;
unsigned int pos = 0;
udf_t *p_udf;
udf_dirent_t *p_udf_root;
char buffer[MAX*DVDCSS_BLOCK_SIZE];
int loop = 1;
if (argc != 3) {
printf("usage:\n\t %s <input iso> <output iso>\n", argv[0]);
return 1;
}
/* figure out how big the ISO image is */
if (stat(argv[1], &stat_buf) < 0) {
perror("failed to stat input iso");
return 1;
}
total_blocks = stat_buf.st_blocks / 4;
// start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_udf = udf_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
if (!(p_udf_root = udf_get_root(p_udf, true, 0))) {
fprintf(stderr, "couldn't find / in %s\n", argv[1]);
return 1;
}
find_start_blocks(p_udf, p_udf_root);
sort(start_blocks.begin(), start_blocks.end());
/* for(vector<int>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %d\n", *it);
} */
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
/* prep output file */
if ((fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) {
printf("failed to open output file\n");
return 1;
}
/* if not scrambled skip! */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int end = start_blocks[0];
// start_blocks.pop_back();
start_blocks.erase(start_blocks.begin());
printf("current position = %u, next sync point at %u\n", pos, end);
// don't dvdcss_seek first time around or last element (last block of dvd
if (pos) {
printf("syncing at position %u\n", pos);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %d: %s\n", pos, dvdcss_error(input));
}
} else {
printf("not seeking as at beginning, no sync points yet\n");
}
while (pos < end) {
printf("current pos = %u\n", pos);
if (pos + len > end) {
printf("going to a read shorter then %d as end is near\n", len);
len = end - pos;
}
printf("going to read %d\n", len);
if ((blocks_read = dvdcss_read(input, buffer, len, DVDCSS_READ_DECRYPT)) != len) {
if (start_blocks.empty()) {
loop = 0;
}
fprintf(stderr, "didn't read %d blocks! read %d blocks\n", len, blocks_read);
}
printf("read %d\n", blocks_read);
pos += blocks_read;
if (write(fd, buffer, 2048 * blocks_read) != 2048 * blocks_read) {
fprintf(stderr, "write didn't write enough\n");
return 0;
}
}
}
}
spotter
18th October 2010, 20:30
this version works on my test iso (mad men season 1 disc 1), it seems dvdcss_read() doesn't like being called before a dvdcss_seek() on a VOB file, so use plain read before we get to the first VOB.
on my machine takes about 5 minutes to read/decode/write a ~7GB iso w/ css scrambled sectors (or about 36 MB/s in total IO as to same disk)
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/udf.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <ctype.h>
using namespace std;
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
vector<int> start_blocks;
#define MAX 1000
static void add_block(const udf_dirent_t *p_udf_dirent)
{
char *psz_fname=(char *) udf_get_filename(p_udf_dirent);
int len = strlen(psz_fname);
unsigned int start;
for(int i=0; i < len; i++) {
psz_fname[i] = tolower(psz_fname[i]);
}
if (! strcmp(psz_fname + (len-3), "vob")) {
start = udf_get_start_block(p_udf_dirent);
start_blocks.push_back(start);
printf("start block for %s = %u\n", psz_fname, start);
} else {
printf("didn't match on name %s\n", psz_fname);
}
}
void find_start_blocks(udf_t *p_udf, udf_dirent_t *p_udf_dirent)
{
if (!p_udf_dirent)
return;
while(udf_readdir(p_udf_dirent)) {
if (udf_is_dir(p_udf_dirent)) {
udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent) {
find_start_blocks(p_udf, p_udf_dirent2);
}
} else {
add_block(p_udf_dirent);
}
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
int fd_in;
struct stat stat_buf;
int total_blocks;
unsigned int pos = 0;
udf_t *p_udf;
udf_dirent_t *p_udf_root;
char * buffer;
unsigned int preamble;
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc != 3) {
printf("usage:\n\t %s <input iso> <output iso>\n", argv[0]);
return 1;
}
/* figure out how big the ISO image is */
if (stat(argv[1], &stat_buf) < 0) {
perror("failed to stat input iso");
return 1;
}
total_blocks = stat_buf.st_size / 2048;
if (stat_buf.st_size != (long long) total_blocks * 2048) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_udf = udf_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
if (!(p_udf_root = udf_get_root(p_udf, true, 0))) {
fprintf(stderr, "couldn't find / in %s\n", argv[1]);
return 1;
}
find_start_blocks(p_udf, p_udf_root);
sort(start_blocks.begin(), start_blocks.end());
/* for(vector<int>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %d\n", *it);
} */
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
/* prep output file */
if ((fd = open(argv[2], O_RDWR | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)) < 0) {
printf("failed to open output file\n");
return 1;
}
if ((fd_in = open(argv[1], O_RDONLY)) < 0) {
printf("failed to open output file\n");
return 1;
}
printf("fd_in = %d\n", fd_in);
/* if not scrambled skip! */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
if (preamble > MAX) {
printf("preamble is too big (%u)\n", preamble);
return 0;
}
pos = read(fd_in, buffer, preamble*2048);
if (pos != preamble * 2048) {
printf("didn't read enough (%u vs %d)\n", preamble*2048, pos);
return 0;
}
if (write(fd, buffer, pos) != pos) {
printf("didn't write enough\n");
return 0;
}
pos = pos / 2048;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int end = start_blocks[0];
// start_blocks.pop_back();
start_blocks.erase(start_blocks.begin());
printf("current position = %u, next sync point at %u\n", pos, end);
// don't dvdcss_seek first time around or last element (last block of dvd
printf("syncing at position %u\n", pos);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %d: %s\n", pos, dvdcss_error(input));
/* } else {
printf("not seeking as at beginning, no sync points yet\n"); */
}
while (pos < end) {
//printf("current pos = %u\n", pos);
if (pos + len > end) {
//printf("going to a read shorter then %d as end is near\n", len);
len = end - pos;
}
//printf("going to read %d\n", len);
if ((blocks_read = dvdcss_read(input, buffer, len, DVDCSS_READ_DECRYPT)) != len) {
fprintf(stderr, "didn't read %d blocks! read %d blocks\n", len, blocks_read);
}
//printf("read %d\n", blocks_read);
pos += blocks_read;
if (write(fd, buffer, 2048 * blocks_read) != 2048 * blocks_read) {
fprintf(stderr, "write didn't write enough\n");
return 0;
}
}
}
}
spotter
19th October 2010, 07:58
and here's a version that does all the writing in place (i.e. just overwrites the blocks that are scrambled by CSS after descrambling them).
caveat: it seems much slower on machine (though probably also because the reads/writes were from different file systems, albiet on same block device)
this version can also chew up and destroy your iso images, so comes with no warranty that it works, only tested on one iso image so far.
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/udf.h>
#include <vector>
#include <algorithm>
#include <string.h>
#include <ctype.h>
using namespace std;
vector<int> start_blocks;
#define MAX 2000
static void add_block(const udf_dirent_t *p_udf_dirent)
{
char *psz_fname=(char *) udf_get_filename(p_udf_dirent);
int len = strlen(psz_fname);
unsigned int start;
for(int i=0; i < len; i++) {
psz_fname[i] = tolower(psz_fname[i]);
}
if (! strcmp(psz_fname + (len-3), "vob")) {
start = udf_get_start_block(p_udf_dirent);
start_blocks.push_back(start);
printf("start block for %s = %u\n", psz_fname, start);
} else {
printf("didn't match on name %s\n", psz_fname);
}
}
void find_start_blocks(udf_t *p_udf, udf_dirent_t *p_udf_dirent)
{
if (!p_udf_dirent)
return;
while(udf_readdir(p_udf_dirent)) {
if (udf_is_dir(p_udf_dirent)) {
udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent) {
find_start_blocks(p_udf, p_udf_dirent2);
}
} else {
add_block(p_udf_dirent);
}
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
struct stat stat_buf;
int total_blocks;
unsigned long long pos = 0;
udf_t *p_udf;
udf_dirent_t *p_udf_root;
char * buffer;
unsigned long long preamble;
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc != 2) {
printf("usage:\n\t %s <input iso>\n", argv[0]);
return 1;
}
/* figure out how big the ISO image is */
if (stat(argv[1], &stat_buf) < 0) {
perror("failed to stat input iso");
return 1;
}
total_blocks = stat_buf.st_size / DVDCSS_BLOCK_SIZE;
if (stat_buf.st_size != (long long) total_blocks * DVDCSS_BLOCK_SIZE) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_udf = udf_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
if (!(p_udf_root = udf_get_root(p_udf, true, 0))) {
fprintf(stderr, "couldn't find / in %s\n", argv[1]);
return 1;
}
find_start_blocks(p_udf, p_udf_root);
sort(start_blocks.begin(), start_blocks.end());
/* for(vector<int>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %d\n", *it);
} */
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
if ((fd = open(argv[1], O_RDWR)) < 0) {
printf("failed to open input/output file\n");
return 1;
}
/* if not scrambled skip! */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
lseek64(fd, preamble*DVDCSS_BLOCK_SIZE, SEEK_SET);
pos = preamble;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int end = start_blocks[0];
start_blocks.erase(start_blocks.begin());
printf("syncing at position = %llu, next sync point at %u\n", pos, end);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %llu: %s\n", pos, dvdcss_error(input));
}
while (pos < end) {
int read_size;
char * tmp_buffer;
int reseek;
if (pos + len > end) {
len = end - pos;
}
read_size = len * DVDCSS_BLOCK_SIZE;
if ((blocks_read = read(fd, buffer, read_size)) != read_size) {
printf("short read, not handled yet\n");
return 1;
}
tmp_buffer = buffer;
reseek = 0;
for(int index = 0; index < len; index++) {
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
if (dvdcss_seek(input, pos+index, DVDCSS_NOFLAGS) < 0) {
fprintf(stderr, "failed to seek to %llu (index %d): %s\n", pos+index, index, dvdcss_error(input));
return 1;
}
if (dvdcss_read(input, tmp_buffer, 1, DVDCSS_READ_DECRYPT) != 1) {
fprintf(stderr, "dvdcss_read failed\n");
return 1;
}
lseek64(fd, (pos+index) * DVDCSS_BLOCK_SIZE, SEEK_SET);
if (write(fd, tmp_buffer, DVDCSS_BLOCK_SIZE) != DVDCSS_BLOCK_SIZE)
{
printf("failed to write block correctly\n");
return 1;
}
reseek = 1;
}
tmp_buffer = tmp_buffer + DVDCSS_BLOCK_SIZE;
}
pos += len;
if (reseek) {
lseek64(fd, pos * DVDCSS_BLOCK_SIZE, SEEK_SET);
}
}
}
}
spotter
19th October 2010, 18:04
so this seems to work, gives a mostly identical copy to what dvd decrypter outputs.
it seems to differ in non VOB files though. My guess is that it dvd decrypter is making the disk region free and removing rce protection. As that's not the goal of this program, it doesn't do that. Though when I get home later today I will try running my decrypted iso through dvd decrypter and see if it offers to remove the region/rce protection as well and results in a the same disk image as when it works on the encrypted version.
SledgeHammer_999
19th October 2010, 20:48
Shouldn't you implement it using libdvdnav/read(can't remember which) instead? As far as I know, libdvdcss can't handle protection schemes that purposedly corrupt the files/filesystem, like Arcos, directly. If I understand correctly dvdnav/read reads the IFO file and skips the corrupted sectors according to it.
This has the potential to be an excellent open-source and free Dvd decrypter...
I will be following this.
JohnAStebbins
19th October 2010, 21:44
Should you implement it using libdvdnav/read(can't remember which) instead? As far as I know, libdvdcss can't handle protection schemes that purposedly corrupt the files/filesystem, like Arcos, directly. If I understand correctly dvdnav/read reads the IFO file and skips the corrupted sectors according to it.
This has the potential to be an excellent open-source and free Dvd decrypter...
I will be following this.
I've thought about doing this, but libdvdnav doesn't have a way to query where a block came from after reading it. So once you have the data, there's no way to know where you need to write it to the image you are creating. You would have to add an api to libdvdnav to retrieve this information.
Then there's the problem of deciding when you have all the necessary data. Reading it in the order libdvdnav gives it means you are skipping over things. Some of those things are valid data that can be accessed if you start with different initial conditions. You have to essentially rip a title multiple times with all variations of initial conditions (angles, multiple entry points, etc.). To make that efficient, you would have to keep a bitmap of blocks already visited and a cache of all nav commands for each block so you don't have to re-read everything multiple times.
That's about as far as I got in thinking about it before deciding to move on to something more interesting :D
spotter
20th October 2010, 05:53
Shouldn't you implement it using libdvdnav/read(can't remember which) instead? As far as I know, libdvdcss can't handle protection schemes that purposedly corrupt the files/filesystem, like Arcos, directly. If I understand correctly dvdnav/read reads the IFO file and skips the corrupted sectors according to it.
This has the potential to be an excellent open-source and free Dvd decrypter...
I will be following this.
in this program, all I'm doing is
1) rekey the css key at the start of each VOB.
2) whenever there's a block that is scrambled with CSS, descramble it and right it back out.
the idea here is that between lsdvd/ddrescue we don't care if the disk has corrupted sectors, ddrescue will just skip over those) and then my program will descramble the css scrambled sectors.
right now, an issue I have is how to deal with RCE type protection. It's not the biggest deal in the world (VLC handles these decrypted images just fine when mounted), but would be nice. my gut feeling is that it would be a second program that knows how to parse the IFO structure and "zero out" the commands that make up RCE, but as I have no clue about RCE at this point in time, unsure how to proceed in regards to it.
spotter
20th October 2010, 06:07
though on this subject, in doing my hexdiff's of my copy against a copy produced by dvd decrypter, I noticed some value that was fe being changed to 00
according to this
http://en.wikipedia.org/wiki/DVD_region_code#Circumvention
that makes perfect sense. wondering if i'd edit them what that would do to RCE. My initial hope was that if I don't try to play with the regionness of the DVD, RCE type protections would just work fine and ignore it as the image is still region coded. That seemed to fail.
spotter
20th October 2010, 06:27
just hexedited the region code, didn't help (somewhat as expected).
spotter
20th October 2010, 06:32
wondering if my code is buggy. acc to dvdcss_read() it should be safe to run on IFO files and the like, but I'm wondering if they lied. going to change my program to make a record of every start/end block and only do css descrambling on the VOB files.
spotter
20th October 2010, 18:52
here's version 5, which is mostly a test (doesn't do any write out) to figure out which non vob blocks have the css bit set, just coded up while on a conference call at work. will test it when I get home
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/udf.h>
#include <vector>
#include <map>
#include <algorithm>
#include <string.h>
#include <ctype.h>
using namespace std;
#define MAX 2000
#define CEILING(x, y) ((x+(y-1))/y) //ripped from libudf
vector<unsigned long long> start_blocks;
map<unsigned long long, unsigned long long> end_blocks;
static void add_block(const udf_dirent_t *p_udf_dirent)
{
char *psz_fname=(char *) udf_get_filename(p_udf_dirent);
int len = strlen(psz_fname);
unsigned long long file_length;
unsigned long long start;
start = udf_get_start_block(p_udf_dirent);
//-1 as start block is included in count of blocks
end_blocks[start] = start - 1 + CEILING(udf_get_file_length(p_udf_dirent), DVDCSS_BLOCK_SIZE);
printf("%s: %llu->%llu\n", psz_fname, start, end_blocks[start]);
for(int i=0; i < len; i++) {
psz_fname[i] = tolower(psz_fname[i]);
}
if (! strcmp(psz_fname + (len-3), "vob")) {
start_blocks.push_back(start);
}
}
void find_start_blocks(udf_t *p_udf, udf_dirent_t *p_udf_dirent)
{
if (!p_udf_dirent)
return;
while(udf_readdir(p_udf_dirent)) {
if (udf_is_dir(p_udf_dirent)) {
udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent) {
find_start_blocks(p_udf, p_udf_dirent2);
}
} else {
add_block(p_udf_dirent);
}
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
struct stat stat_buf;
int total_blocks;
unsigned long long pos = 0;
udf_t *p_udf;
udf_dirent_t *p_udf_root;
char * buffer;
unsigned long long preamble;
unsigned long long count = 0;
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc != 2) {
printf("usage:\n\t %s <input iso>\n", argv[0]);
return 1;
}
/* figure out how big the ISO image is */
if (stat(argv[1], &stat_buf) < 0) {
perror("failed to stat input iso");
return 1;
}
total_blocks = stat_buf.st_size / DVDCSS_BLOCK_SIZE;
if (stat_buf.st_size != (long long) total_blocks * DVDCSS_BLOCK_SIZE) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_udf = udf_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
if (!(p_udf_root = udf_get_root(p_udf, true, 0))) {
fprintf(stderr, "couldn't find / in %s\n", argv[1]);
return 1;
}
find_start_blocks(p_udf, p_udf_root);
sort(start_blocks.begin(), start_blocks.end());
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
// if ((fd = open(argv[1], O_RDWR)) < 0) {
if ((fd = open(argv[1], O_RDONLY)) < 0) {
printf("failed to open input/output file\n");
return 1;
}
/* if not scrambled skip! */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
lseek64(fd, preamble*DVDCSS_BLOCK_SIZE, SEEK_SET);
pos = preamble;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int cur = pos;
int end = start_blocks[0];
start_blocks.erase(start_blocks.begin());
//printf("syncing at position = %llu, next sync point at %u\n", pos, end);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %llu: %s\n", pos, dvdcss_error(input));
}
while (pos < end) {
int read_size;
char * tmp_buffer;
int reseek;
if (pos + len > end) {
len = end - pos;
}
read_size = len * DVDCSS_BLOCK_SIZE;
if ((blocks_read = read(fd, buffer, read_size)) != read_size) {
printf("short read, not handled yet\n");
return 1;
}
tmp_buffer = buffer;
reseek = 0;
for(int index = 0; index < len; index++) {
char block[2048];
bcopy(tmp_buffer, block, 2048);
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
int skip=0;
if (pos + index > end_blocks[cur]) {
printf("should skipping decode of supposed encrypted block (%llu) as not within VOB\n", pos+index);
skip=1;
//continue;
}
count++;
if (dvdcss_seek(input, pos+index, DVDCSS_NOFLAGS) < 0) {
fprintf(stderr, "failed to seek to %llu (index %d): %s\n", pos+index, index, dvdcss_error(input));
return 1;
}
if (dvdcss_read(input, tmp_buffer, 1, DVDCSS_READ_DECRYPT) != 1) {
fprintf(stderr, "dvdcss_read failed\n");
return 1;
}
if (skip) {
printf("testing block we would skip\n");
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
printf("dvdcss \"decoded\" block still has bit set\n");
if (!memcmp(tmp_buffer, block, 2048)) {
printf("dvdcss \"decoded\" block didn't change!\n");
} else {
printf("dvdcss \"decoded\" block did change!\n");
}
} else {
printf("dvdcss \"decoded\" block got bit removed!\n");
}
}
/* lseek64(fd, (pos+index) * DVDCSS_BLOCK_SIZE, SEEK_SET);
if (write(fd, tmp_buffer, DVDCSS_BLOCK_SIZE) != DVDCSS_BLOCK_SIZE)
{
printf("failed to write block correctly\n");
return 1;
} */
reseek = 1;
}
tmp_buffer = tmp_buffer + DVDCSS_BLOCK_SIZE;
}
pos += len;
if (reseek) {
lseek64(fd, pos * DVDCSS_BLOCK_SIZE, SEEK_SET);
}
}
}
printf("descrambled %llu blocks\n", count);
printf("\n");
return 0;
}
spotter
21st October 2010, 00:45
ok, so I was wrong above, the decryption worked perfectly. I just had it mounted in windows and Daemon Tools was set to be region free so RCE kicked it (as it should).
set it to region 1 and everything was great.
spotter
21st October 2010, 05:32
here's my current version for now, seems to work pretty well.
what did I learn here? it seems that Gilmore Girls Season 1 Disk 2 has VTS_#_0.VOB files of 0 length (and hence only 1 block) in the middle of an area that would be contiguous with another VOB.
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/udf.h>
#include <vector>
#include <map>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <errno.h>
using namespace std;
#define MAX 2000
#define CEILING(x, y) ((x+(y-1))/y) //ripped from libudf
vector<unsigned long long> start_blocks;
map<unsigned long long, unsigned long long> end_blocks;
map<unsigned long long, char *> start_map;
ssize_t my_write(int fd, const void * buf, size_t count)
{
int ret;
size_t my_count = count;
char * my_buf = (char *) buf;
while (my_count) {
if (my_count != count) {
printf("looped in my_write, last write = %d\n", ret);
}
if ((ret = write(fd, my_buf, my_count)) == -1) {
if (errno != EINTR) {
perror("write failed!");
goto out;
}
} else {
my_count -= ret;
my_buf += ret;
}
}
ret = count;
out:
return ret;
}
static void add_block(const udf_dirent_t *p_udf_dirent)
{
char *psz_fname=(char *) udf_get_filename(p_udf_dirent);
char *name = strdup(psz_fname);
int len = strlen(psz_fname);
unsigned long long file_length;
unsigned long long start;
unsigned long long blocks;
start = udf_get_start_block(p_udf_dirent);
blocks = CEILING(udf_get_file_length(p_udf_dirent), DVDCSS_BLOCK_SIZE);
start_map[start] = name;
if (blocks == 0) {
//file length of 0 would result in a blocks of 0, and don't want
//to subtract one from it.
end_blocks[start] = start;
} else {
//-1 as start block is included in count of blocks
end_blocks[start] = start - 1 + CEILING(udf_get_file_length(p_udf_dirent), DVDCSS_BLOCK_SIZE);
}
printf("%s: %llu->%llu\n", psz_fname, start, end_blocks[start]);
for(int i=0; i < len; i++) {
psz_fname[i] = tolower(psz_fname[i]);
}
if (! strcmp(psz_fname + (len-3), "vob")) {
if (blocks) {
// if (find(start_blocks.begin(), start_blocks.end(), start) == start_blocks.end()) {
start_blocks.push_back(start);
}
}
}
void find_start_blocks(udf_t *p_udf, udf_dirent_t *p_udf_dirent)
{
if (!p_udf_dirent)
return;
while(udf_readdir(p_udf_dirent)) {
if (udf_is_dir(p_udf_dirent)) {
udf_dirent_t *p_udf_dirent2 = udf_opendir(p_udf_dirent);
if (p_udf_dirent) {
find_start_blocks(p_udf, p_udf_dirent2);
}
} else {
add_block(p_udf_dirent);
}
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
off64_t disc_len;
int total_blocks;
unsigned long long pos = 0;
udf_t *p_udf;
udf_dirent_t *p_udf_root;
char * buffer;
unsigned long long preamble;
unsigned long long count = 0;
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc < 2) {
printf("usage:\n\t %s <input iso>\n", argv[0]);
return 1;
}
if ((fd = open(argv[1], O_RDWR)) < 0) {
printf("failed to open input/output file\n");
return 1;
}
/* figure out how big the ISO image is */
if ((disc_len = lseek64(fd, 0, SEEK_END)) < 0) {
perror("lseek64 failed");
return 1;
}
total_blocks = disc_len / DVDCSS_BLOCK_SIZE;
if (disc_len != (long long) total_blocks * DVDCSS_BLOCK_SIZE) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_udf = udf_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
if (!(p_udf_root = udf_get_root(p_udf, true, 0))) {
fprintf(stderr, "couldn't find / in %s\n", argv[1]);
return 1;
}
find_start_blocks(p_udf, p_udf_root);
sort(start_blocks.begin(), start_blocks.end());
for(vector<unsigned long long>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %llu\n", *it);
}
for(map<unsigned long long, char *>::iterator p = start_map.begin(); p != start_map.end(); p++) {
printf("%s : %llu\n", p->second, p->first);
}
if (argc == 3 && !strcmp(argv[2], "-test")) {
return 0;
}
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
/* if not scrambled skip! */
/* this doesn't do anything on iso input */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
lseek64(fd, preamble*DVDCSS_BLOCK_SIZE, SEEK_SET);
pos = preamble;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int cur = pos;
int end = start_blocks[0];
start_blocks.erase(start_blocks.begin());
//printf("syncing at position = %llu, next sync point at %u\n", pos, end);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %llu: %s\n", pos, dvdcss_error(input));
}
while (pos < end) {
int read_size;
char * tmp_buffer;
int reseek;
if (pos + len > end) {
len = end - pos;
}
read_size = len * DVDCSS_BLOCK_SIZE;
if ((blocks_read = read(fd, buffer, read_size)) != read_size) {
printf("short read, not handled yet\n");
return 1;
}
tmp_buffer = buffer;
reseek = 0;
for(int index = 0; index < len; index++) {
char block[2048];
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
int skip=0;
if (pos + index > end_blocks[cur]) {
printf("should skipping decode of supposed encrypted block (%llu) as not within VOB\n", pos+index);
bcopy(tmp_buffer, block, 2048);
skip=1;
}
count++;
if (dvdcss_seek(input, pos+index, DVDCSS_NOFLAGS) < 0) {
fprintf(stderr, "failed to seek to %llu (index %d): %s\n", pos+index, index, dvdcss_error(input));
return 1;
}
if (dvdcss_read(input, tmp_buffer, 1, DVDCSS_READ_DECRYPT) != 1) {
fprintf(stderr, "dvdcss_read failed\n");
return 1;
}
if (skip) {
printf("testing block we are skipping anyways\n");
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
printf("dvdcss \"decoded\" block still has bit set\n");
if (!memcmp(tmp_buffer, block, 2048)) {
printf("dvdcss \"decoded\" block didn't change!\n");
} else {
printf("dvdcss \"decoded\" block changed!\n");
}
} else {
printf("dvdcss \"decoded\" block got bit removed!\n");
}
} else {
lseek64(fd, (pos+index) * DVDCSS_BLOCK_SIZE, SEEK_SET);
if (my_write(fd, tmp_buffer, DVDCSS_BLOCK_SIZE) < 0) {
return 1;
}
reseek = 1;
}
}
tmp_buffer = tmp_buffer + DVDCSS_BLOCK_SIZE;
}
pos += len;
if (reseek) {
lseek64(fd, pos * DVDCSS_BLOCK_SIZE, SEEK_SET);
}
}
}
printf("descrambled %llu blocks\n", count);
printf("\n");
return 0;
}
sl1pkn07
21st October 2010, 21:58
run on linux?
spotter
21st October 2010, 22:24
that's the point.
spotter
22nd October 2010, 03:33
so I just tried my methodology on a DVD from castle season 2 (2nd to be precise). This set seems to be protected by a number of schemes, bad sectors, crazy IFOs, crazy VOBs (i.e. that reference same blocks on the disc). My methodology of
1) authenticating the drive (lsdvd. though in this case lsdvd fails too as can't parse the IFOs, so resorted to vlc)
2) ripping the disc with ddrescue (takes a while, as slow to iterate through the bad sectors)
3) decoding the vob with my tool
seems to result in a DVD that is fully playable.
setarip_old
22nd October 2010, 04:36
@spotter
Hi! seems to result in a DVD that is fully playable.
I'm just a curious bystander on this one. Does your project yield a full DVD "package", including menus, or is it "movie only"?
spotter
22nd October 2010, 04:54
@spotter
Hi!
I'm just a curious bystander on this one. Does your project yield a full DVD "package", including menus, or is it "movie only"?
all it does is make an ISO image that is exactly the same as the initial one but without any encryption. so all their protection methods are still in place (RCE...), except doesn't make a difference for any player that can actually play a DVD.
This issue was because if I make an ISO image with the CSS scrambled sectors still embeded within it, I can't mount it with programs like daemon tools and have it play as many programs see that they can't authenticate the drive/disc so skip doing the deCSS. With this, no need, as the CSS is removed.
setarip_old
22nd October 2010, 06:46
the idea here is that between lsdvd/ddrescue we don't care if the disk has corrupted sectors, ddrescue will just skip over those) and then my program will descramble the css scrambled sectors.Although I'd be delighted to hear of your complete success, as I've suggested to other Linux users at Doom9, who previously attempted the same thing you are pursuing (FULL DISC DVD backup), if the only things involved in the world of DVD protection were CSS and bad sectoring, there'd be no need for the ongoing updates and modifications to several commercial DVD "ripping" programs.
To get a better sense of what I'm referring to, you might visit the "DVDFab" and "Slysoft"/AnyDVD sites and forums to see which DVDs required
the authors to modify their programs to deal with protection schemes that go well beyond simple CSS and simple bad sectoring.
By all means keep us apprised of your progress ;>}
spotter
22nd October 2010, 07:02
if you can point me to DVDs (region 1) that have been known to be problematic I can try taking them out of the library and seeing what happens. I'm not trying to play any games with IFOs (not even playing with region coding), so unsure why my method wouldn't work. (now, if you'd extract it to files, then you might have a problem, especially with it blowing up way beyond what can fit on a DVD due to all these shared blocks).
I can tell you that I see people complain about Castle, and I can tell you my program handled it without a problem. Now, I also know very little about any of the specs that make up DVDs (UDF, through dvd), so this is mostly a hack, but the main idea is sound I think. 1) grab a copy w/ css scrambled sectors 2) descramble those sectors.
there's nothing any DVD mastering process can do to stop step 1 as long as they can be played on a computer. they can make it more painful by having invalid sectors, but ddrescue just skips over them. now life is more difficult if your DVD is scratched and has valid sectors that are unreadable, how do you know if you got it all or not, but that's not a problem i'm dealing with at the moment.
step 2 is the hackish part, as I'm making 2 assumptions
a) can always rekey the css key by calling dvdcss_seek() to the first block of a vob file (and in fact, this seems to be how libdvdread works, so if the mastering process could change this, it might break all linux players)
b) that vob files are contiguous on the medium. namely if the file start at block X and is Y blocks long, I can decrypt any scrambled sectors from X through X+Y-1 with the key I got.
if either of these assumptions fail, my program will fail. But I haven't see anything that indicates they do fail.
spotter
22nd October 2010, 07:15
so put iron man 2 on hold at the library, hopefully get it early next week. that's a DVD that seemed to cause problems. let's see if my naive approach works on it as well.
setarip_old
22nd October 2010, 08:25
You might want to try some of these (culled from the websites I had suggested you check out):
"Dora the Explorer: Super Silly Fiesta", Region 2 and 4 (UK & Australia)
"New Moon", R1, US
"Precious", R1, US
"Angel & Demons (rental)", Japan
"Insanity Workout"
and "One on One with Tony Horton" (US)
"Haunting in Connecticut", R1, US
"Transporter 3", R2, Germany
"Underbelly -
a tale of two cities", R4, Australia
"Baader Meinhof Komplex", R2, Germany
"The Closet", UK
"Prince of Persia The Sands of Time"
"Region 1 DVD of Leap Year"
"Leap Year" (US)
"Alice in Wonderland"
"Iron Man 2" (US)
"Shutter Island"
spotter
22nd October 2010, 13:41
well, won't be able to get any non region 1 disc, but iron man 2 shood come soon, will also put leap year, shutter island, prince of persia...on hold. (in some ways better than iron man 2, as more willing to sit trough a complete watching, as never seen).
But as I said, I have no idea how they could impact my methodology beyond making it take longer, as I never even look in any files expecting a certain structure (i.e. IFO).
beandog
22nd October 2010, 17:31
Although I'd be delighted to hear of your complete success, as I've suggested to other Linux users at Doom9, who previously attempted the same thing you are pursuing (FULL DISC DVD backup), if the only things involved in the world of DVD protection were CSS and bad sectoring, there'd be no need for the ongoing updates and modifications to several commercial DVD "ripping" programs.
Am I missing something? Backing up a full DVD isn't hard. vobcopy comes to mind.
spotter
22nd October 2010, 18:06
that could possibly work, but notice this from castle season 2
root@nas:/# df -h /mnt
Filesystem Size Used Avail Use% Mounted on
/dev/loop0 7.2G 7.2G 0 100% /mnt
vs
root@nas:/# du -hsc /mnt
21G /mnt
21G total
i.e. my version doesn't care that the file system appears to have 21GB of data on it, I only operate on 7+GB that are actually in the image.
vobcopy will just copy everything, it has no idea of DVD security. I have no idea of DVD security either, but as operate only on the blocks (have very little concept of the file system), I'm left with the same size as the original (in fact, just overwrite the css scrambled blocks w/ the decss unscrambled versions)
spotter
22nd October 2010, 20:17
so if I were a movie studio, how would I try to attack my program? I'd try to create invalid VOBs that cover areas that include valid IFOs and construct the IFO in such a way that if one tries to descramble it with CSS it gets corrupted.
don't know if this is possible, but seems a good way to mess things up.
nevragain
23rd October 2010, 03:43
Aside from the method you describe bad sectors (that the IFO instruct the player to skip over) there are two more I have heard described.
Using an invalid disc label (characters not permitted).
The use of a corrupted file system.
I believe that the corruption related to UDF based on the theory that hardware dvd players would see only the ISO file system.
This corruption may have involved fake files too large to fit on the disc or the use of fake files over the limit permitted in UDF.
This Netflix list also included some dvd with extra copy protection.
http://www.netflix.com/FAQ?p_faqid=2462
spotter
24th October 2010, 00:29
yes, but corrupted file system doesn't matter to me, as I don't really need to look at the file system beyond to see start of VOBs. With that said, I have the ability to only view the ISO file system as well. With that said, on castle DVD, just mounted it in linux as both iso9660 and udf and it sees same messed upness.
and in worse case, all I'd have to do to get around the way I say the studios could attack this is to have the ability to create the file system manually.
i.e. in the worse case where I don't know which vob blocks are good and which ifo blocks are good. all I have to do is is recreate a file system with the same overlapped corruption (how I'd do this, don't know, but not that difficult I'd imagine)
1) read all IFO related blocks - i.e. assume they are all valid
2) read all VOB related blocks - i.e. also assume they are all valid
3) create corrupted file system with VOB files sharing the blocks as appropriate
4) add all the IFO related blocks and the files to the file system (assumption being that these are minimal in size and hence even if share blocks, doesn't matter much)
this approach really discounts the file system and just looks at the disc as a raw block device.
spotter
24th October 2010, 00:55
basically, with my experience now, making a full image copy of any DVD should be easy for software like dvdfab/anydvd and should need no changes over time. only thing that they should have to adapt to is if one 1) wants a file system rip (i.e. not an image) or 2) wants a specific title from the dvd. could be wrong, but playing castle which has numerous protections without a problem using my method.
though, in regards to title, I think it should also be fairly straightforward. namely, just present the DVD menu to the user, and play through it till you get to the title the user wants and then just record those vm registers and play it through the same and record all the correct blocks and create the correct IFO for it. (doubt I'd ever do this, at last without help, as not what I'm aiming at).
marshalleq
24th October 2010, 21:54
I think this all sounds very compelling. Presumably once you'd made a descrambled ISO, you could run some DVD fab over it anyway?
Expand it to support blu ray then we'll all be very happy ;)
But please, keep up the great work!
spotter
24th October 2010, 22:22
1) yes dvdfab works over the decrypted DVD if you want a file system rip tested
2) re blueray, I doubt file system structural protections are what they are dealing with, isn't it mostly bd+ these days \
3) running into crashes when running against udf file system on some DVDs (30 Rock and Community to be percise), I easily get the same information with the iso9660 file system so going to try with it. so there will probably be a v6 soon.
spotter
24th October 2010, 23:03
version 6, works based on iso9660 file system (also doesn't require any patches to libcdio)
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <vector>
#include <map>
#include <algorithm>
#include <string.h>
#include <ctype.h>
#include <errno.h>
using namespace std;
#define MAX 2000
#define CEILING(x, y) ((x+(y-1))/y) //ripped from libudf
vector<unsigned long long> start_blocks;
map<unsigned long long, unsigned long long> end_blocks;
map<unsigned long long, char *> start_map;
ssize_t my_write(int fd, const void * buf, size_t count)
{
int ret;
size_t my_count = count;
char * my_buf = (char *) buf;
while (my_count) {
if (my_count != count) {
printf("looped in my_write, last write = %d\n", ret);
}
if ((ret = write(fd, my_buf, my_count)) == -1) {
if (errno != EINTR) {
perror("write failed!");
goto out;
}
} else {
my_count -= ret;
my_buf += ret;
}
}
ret = count;
out:
return ret;
}
void find_start_blocks(iso9660_t *p_iso)
{
CdioList_t *p_entlist;
CdioListNode_t *p_entnode;
if (!p_iso)
return;
p_entlist = iso9660_ifs_readdir (p_iso, "/video_ts/");
if (p_entlist) {
_CDIO_LIST_FOREACH (p_entnode, p_entlist)
{
unsigned long long start;
unsigned long long blocks;
char filename[4096];
int len;
iso9660_stat_t *p_statbuf =
(iso9660_stat_t *) _cdio_list_node_data (p_entnode);
iso9660_name_translate(p_statbuf->filename, filename);
len = strlen(filename);
if ( ! (2 == p_statbuf->type) ) {
if (! strcmp(filename + (len-3), "vob")) {
start = p_statbuf->lsn;
blocks = CEILING(p_statbuf->size, DVDCSS_BLOCK_SIZE);
start_map[start] = strdup(filename);
if (blocks == 0) {
//file length of 0 would result in a blocks of 0, and don't want
//to subtract one from it.
end_blocks[start] = start;
} else {
//-1 as start block is included in count of blocks
end_blocks[start] = start - 1 + blocks;
}
printf("%s: %llu->%llu (%llu blocks)\n", filename, start, end_blocks[start], blocks);
if (blocks) {
if (find(start_blocks.begin(), start_blocks.end(), start) == start_blocks.end()) {
start_blocks.push_back(start);
}
}
}
}
}
_cdio_list_free (p_entlist, true);
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
int flags = O_RDWR;
off64_t disc_len;
int total_blocks;
unsigned long long pos = 0;
iso9660_t * p_iso;
char * buffer;
unsigned long long preamble;
unsigned long long count = 0;
if (argc == 3 && !strcmp(argv[2], "-test")) {
flags = O_RDONLY;
}
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc < 2) {
printf("usage:\n\t %s <input iso>\n", argv[0]);
return 1;
}
if ((fd = open(argv[1], flags)) < 0) {
printf("failed to open input/output file\n");
return 1;
}
/* figure out how big the ISO image is */
if ((disc_len = lseek64(fd, 0, SEEK_END)) < 0) {
perror("lseek64 failed");
return 1;
}
total_blocks = disc_len / DVDCSS_BLOCK_SIZE;
if (disc_len != (long long) total_blocks * DVDCSS_BLOCK_SIZE) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_iso = iso9660_open(argv[1])) == NULL) {
fprintf(stderr, "couldn't open %s as UDF\n", argv[1]);
return 1;
}
find_start_blocks(p_iso);
sort(start_blocks.begin(), start_blocks.end());
for(vector<unsigned long long>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %llu\n", *it);
}
for(map<unsigned long long, char *>::iterator p = start_map.begin(); p != start_map.end(); p++) {
printf("%s : %llu\n", p->second, p->first);
}
if (argc == 3 && !strcmp(argv[2], "-test")) {
return 0;
}
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
fprintf(stderr, "dvdcss_open failed\n");
return 1;
}
/* if not scrambled skip! */
/* this doesn't do anything on iso input */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
lseek64(fd, preamble*DVDCSS_BLOCK_SIZE, SEEK_SET);
pos = preamble;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int cur = pos;
int end = start_blocks[0];
int seeked = DVDCSS_NOFLAGS;
start_blocks.erase(start_blocks.begin());
//printf("syncing at position = %llu, next sync point at %u\n", pos, end);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
fprintf(stderr, "failed to seek to %llu: %s\n", pos, dvdcss_error(input));
seeked = DVDCSS_SEEK_KEY;
}
while (pos < end) {
int read_size;
char * tmp_buffer;
int reseek;
if (pos + len > end) {
len = end - pos;
}
read_size = len * DVDCSS_BLOCK_SIZE;
if ((blocks_read = read(fd, buffer, read_size)) != read_size) {
printf("short read, not handled yet\n");
return 1;
}
tmp_buffer = buffer;
reseek = 0;
for(int index = 0; index < len; index++) {
char block[2048];
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
int skip=0;
if (pos + index > end_blocks[cur]) {
printf("should skipping decode of supposed encrypted block (%llu) as not within VOB\n", pos+index);
bcopy(tmp_buffer, block, 2048);
skip=1;
}
count++;
if (dvdcss_seek(input, pos+index, seeked) < 0) {
fprintf(stderr, "failed to seek to %llu (index %d): %s\n", pos+index, index, dvdcss_error(input));
return 1;
}
seeked = DVDCSS_NOFLAGS;
if (dvdcss_read(input, tmp_buffer, 1, DVDCSS_READ_DECRYPT) != 1) {
fprintf(stderr, "dvdcss_read failed\n");
return 1;
}
if (skip) {
printf("testing block we are skipping anyways\n");
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
printf("dvdcss \"decoded\" block still has bit set\n");
if (!memcmp(tmp_buffer, block, 2048)) {
printf("dvdcss \"decoded\" block didn't change!\n");
} else {
printf("dvdcss \"decoded\" block changed!\n");
}
} else {
printf("dvdcss \"decoded\" block got bit removed!\n");
}
} else {
lseek64(fd, (pos+index) * DVDCSS_BLOCK_SIZE, SEEK_SET);
if (my_write(fd, tmp_buffer, DVDCSS_BLOCK_SIZE) < 0) {
return 1;
}
reseek = 1;
}
}
tmp_buffer = tmp_buffer + DVDCSS_BLOCK_SIZE;
}
pos += len;
if (reseek) {
lseek64(fd, pos * DVDCSS_BLOCK_SIZE, SEEK_SET);
}
}
}
printf("descrambled %llu blocks\n", count);
printf("\n");
return 0;
}
setarip_old
24th October 2010, 23:11
@spotter
A few days ago, I provided you with a list of 15 or 16 DVD titles that initially were not "rippable" by either AnyDVD or DVDFab - and required those programs to be modified in order to successfully backup those titles as full DVD "packages" (not simply "movie-only").
Have you as yet tried to make full DVD "packages" (.ISO image files or file format, including .IFO, .BUPs, and .VOBs) with any of these DVDs?
If so, how successful were you?
spotter
24th October 2010, 23:38
Iron Man 2 (2 disc set) is on hold at the library and in transfer to my local library, assuming it's not scratched, I'll try it out. can only put a limited # on hold so can't test that many so quickly :)
I'm just a lone person doing this for fun (and my local setup)
setarip_old
24th October 2010, 23:49
I'm just a lone person doing this for fun (and my local setup)Understood - I just want you to be aware of what's out there ;>}
spotter
25th October 2010, 23:54
have the new alice in wonderland, testing now.
update1:
- lsdvd complained (as it does for a bunch of disks "Please send bug report - no VTS_TMAPT ?? " but that doesn't impact me. only use lsdvd to authenticate the disk/drive. though it lists 99 titles! (not that I care about that either)
- ddrescue is chugging along. some invalid sectors at the beginning of the disk, but have read about 2GB so far without much issue.
update2:
- ddrescue did its thing, they really corrupt the FS
root@nas:/raid/data/image/recover/DVDs/aiw# df -h /mnt
Filesystem Size Used Avail Use% Mounted on
/dev/loop0 6.8G 6.8G 0 100% /mnt
root@nas:/raid/data/image/recover/DVDs/aiw# du -hsc /mnt
60G /mnt
60G total
i.e. ~7GB of disk size, but files claim 60G.
update3: descrambled 769256 blocks
seems to work without a problem.
setarip_old
26th October 2010, 02:30
seems to work without a problem.Full DVD with menus and extras, or "movie only"?
spotter
26th October 2010, 02:33
full DVD image with messed up file system as well and what ever extra region protection is included. i.e. I don't modify the image at all besides to unscramble the css sectors.
Don't do any tricks, just act like a DVD player, just read all blocks (including bad blocks, but as IFO will never let the dvd player read those blocks it doesn't matter).
Honestly, if we wanted to edi the IFOs to remove those protections, in my scheme it wouldn't be that hard if one knew how, but I don't and have no need to remove extra region protection either.
setarip_old
26th October 2010, 04:33
Sound interesting.
Have you actually burned a (D/L) disc and played it as a full DVD (with functioning menus and extras, as well as movie) on both a standalone DVD player and a PC software DVD player?
spotter
26th October 2010, 04:38
No, just mounted with daemon tools in windows. Don't have a standalone player. Also, for DL layer media, I think layer break issues might be important? I don't have the knowledge to calculate that.
setarip_old
26th October 2010, 05:21
Sorry, I'm a bit unclear about what you've accomplished thusfar.
You've created an .ISO image file that you've mounted with Daemon Tools, correct?
Once it was mounted, were you able to play it properly as a full DVD (with functioning menus and extras, as well as movie) on a PC software DVD player?
If so, this would be a major accomplishment...
spotter
26th October 2010, 05:49
yes to your Q.
lsdvd authenticates the drive/disc - without doing the disk, the dvd disk prevents some sectors from being even able to be read (the css scrambled sectors? unsure really)
ddrescue does a bitexact copy of the disk with the css encrypted sectors scrambled as they are on disc. so now one has an iso image that will play in vlc (if one open the ISO as a file, though VLC's iso support is crappy in my experience).
one can't just mount this iso w/ css scrambled sectors with daemon tools mostly because daemon tools doesn't act like a drive that one can authenticate and most tools assume that any disc in a drive that they can't authenticate to doesn't have css scrambled sectors so won't try to descramble css sectors
my tool just descrambled the css scrambled sectors in the image produced by ddrescue. pretty simple tool.
because I don't really pay attention to the file system, file system corruption doesn't matter to me (such as Alice in Wonderland that looks like it has 60GB of content, in my produced image it also looks like it has 60GB of content) All I need to know from the file system is where it claims the start/end of each vob is. now, this is something they can try to attack, but might break real players for all I know.
I don't think its that big of an accomplishment, it's a pretty naive approach. I think most tools just try to be too smart to create iso images. For example, DVDFab rips the movie to the file system and then creates an iso out of that rip. AnyDVD might do the same. That requires figuring out the titles and only ripping the real titles and editing the IFOs to fix things up and remove things like region protection. I don't need to (or want to) do any of that.
setarip_old
26th October 2010, 07:33
Please clarify. I asked you TWO questions:
1) You've created an .ISO image file that you've mounted with Daemon Tools, correct?
2) Once it was mounted, were you able to play it properly as a full DVD (with functioning menus and extras, as well as movie) on a PC software DVD player?
You responded:yes to your Q.This appears to be a response to my first question.
So would you please specifically answer my second question:
Once it was mounted, were you actually able to successfully play it properly as a full DVD (with functioning menus and extras, as well as movie - not simply as a file/"movie-only") on a PC software DVD player?
Thank you.
spotter
26th October 2010, 07:39
Yes to both. Mplayer classic.
setarip_old
26th October 2010, 07:49
Again, thank you - And best of luck with the other DVDs I listed ;>}
spotter
26th October 2010, 15:21
worked with windows media player 12 in windows 7 as well.
also, I'm not claiming any perfection in my code, very easily it could be buggy, but I think the methodology is sound.
setarip_old
26th October 2010, 17:54
Can you convert your process to run under Windows, so that it can be explored by the masses, including me?
spotter
26th October 2010, 18:33
it should run under cygwin just fine once. It's only library dependencies are libdvdcss and libcdio, and libcdio is probably already available on cygwin and libdvdcss should build easily.
ddrescue is available in cygwin and lsdvd is a simple program so assume it would build as well (though can probably fake it out by just having vlc play the disc for a moment before you ddrescue it)
spotter
26th October 2010, 18:44
the way I build on linux is g++ `pkg-config --cflags --libs libdvdcss libudf libiso9660` <cpp file> (for latest version don't really need the libudf) for those that want to build it
setarip_old
26th October 2010, 19:04
I'm referring to actual Windows (I, for one, know nothing about Linux, cygwin, etc.)
spotter
26th October 2010, 20:09
cygwin is windows. its an application that runs in windows that provides a unix like environment and lets you compile/run many unix programs. someone else would have to build it for that environment, but it should be easy.
SledgeHammer_999
27th October 2010, 13:51
I think it might be possible to compile it using Mingw (http://www.mingw.org/) too!
spotter
27th October 2010, 14:23
probably and mingw is better in my experience (at least I've had better experience compiling handbrakecli w/ mingw than with cygwin). but one would still need a version of ddrescue.
spotter
27th October 2010, 15:56
I can't get an environment setup for mingw easily and don't have time to play with it, if anyone else wants to create a mingw binary, go ahead.
sl1pkn07
28th October 2010, 14:13
thanks man!
(for make you program for linux!)
is possible make the all steps directly with your progarm? (calling lsdvd and ddrescue and make the iso decrypter directly form te drive)
spotter
28th October 2010, 15:46
no, it works directly on the iso. ./a.out <iso image>
what I do
1) lsdvd /dev/dvd
2) ddrescue -v -b 2048 -d /dev/dvd <image.iso> <image.log>
(perhaps adding -r -1 depending if scratched and trying to rescue scratched but valid parts, but makes it hard to distinguish with protection based invalid sectors, so losing proposition if you have a scratched disc of that type)
3) ./a.out <image.iso>
I give no warranties that my program wont mess up the ISO image, could very well be buggy. But I think it works well enough to prove that my naive method is a sound approach for whole disc decryption.
spotter
28th October 2010, 20:19
I should be picking up iron man 2 from library tonight to test on. Also watched Alice in Wonderland through last night, didn't notice any problems.
setarip_old
29th October 2010, 00:38
Also watched Alice in Wonderland through last night, didn't notice any problems.Including all menus and extras, or just "movie only"?
spotter
29th October 2010, 00:41
well, both disney fast play into movie and watching the movie through regular menu
spotter
29th October 2010, 02:13
iron man 2 extras DVD worked fine.
spotter
29th October 2010, 02:31
not watching through the whole video at this moment but disc 1 (the movie) seems to be fine too (windows media player playing the mounted iso w/ daemon tools)
setarip_old
29th October 2010, 03:32
If you burn a disc from the .ISO, would that play properly, on a PC, on a standalone player?
spotter
29th October 2010, 03:33
as mentioned, if it requires dual layer, I'd think that might be an issue. I don't know how to calculate the equivalent of an mds file.
setarip_old
29th October 2010, 03:35
I just had an odd thought (don't ask why) - You're not running AnyDVD or DVDFab while doing any of this, are you?
Again, a very odd thought...
spotter
29th October 2010, 03:40
By definition no. This is all in Linux.
Also, I'm thinking that layer break might not be an issue. imgburn seems to be able to figure it out.
cwl7454
29th October 2010, 09:43
"By definition no"
Rather odd answer
spotter
29th October 2010, 17:01
"By definition no"
Rather odd answer
if I'm running in Linux, how can I anydvd/dvdfab running?
qyot27
30th October 2010, 14:12
In an effort to help on the MinGW side, trying to compile results in the following error:
$ g++ `pkg-config --cflags --libs libdvdcss libiso9660` decrypter.cpp
decrypter.cpp: In function 'int main(int, char**)':
decrypter.cpp:244:34: error: 'bcopy' was not declared in this scope
This was with the 2010.09.07 premade MSys/MinGW environment on the CCCP Wiki, straight as it comes with no additional stuff installed or tweaked. It can compile libdvdcss and libcdio out of the box, but it chokes on the decrypter, with the above error.
spotter
30th October 2010, 23:54
add #include <strings.h> (I just had string.h) or replace bcopy w/ memcpy (and switch src/dest params around)
spotter
31st October 2010, 02:50
updated version, mostly to fix problem compiling described above.
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
#include <stdio.h>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <vector>
#include <map>
#include <algorithm>
#include <string.h>
#include <strings.h>
#include <ctype.h>
#include <errno.h>
using namespace std;
#define MAX 2000
#define CEILING(x, y) ((x+(y-1))/y) //ripped from libudf
vector<unsigned long long> start_blocks;
map<unsigned long long, unsigned long long> end_blocks;
map<unsigned long long, char *> start_map;
ssize_t my_write(int fd, const void * buf, size_t count)
{
int ret;
size_t my_count = count;
char * my_buf = (char *) buf;
while (my_count) {
if (my_count != count) {
printf("looped in my_write, last write = %d\n", ret);
}
if ((ret = write(fd, my_buf, my_count)) == -1) {
if (errno != EINTR) {
perror("write failed!");
goto out;
}
} else {
my_count -= ret;
my_buf += ret;
}
}
ret = count;
out:
return ret;
}
void find_start_blocks(iso9660_t *p_iso)
{
CdioList_t *p_entlist;
CdioListNode_t *p_entnode;
if (!p_iso)
return;
p_entlist = iso9660_ifs_readdir (p_iso, "/video_ts/");
if (p_entlist) {
_CDIO_LIST_FOREACH (p_entnode, p_entlist)
{
unsigned long long start;
unsigned long long blocks;
char filename[4096];
int len;
iso9660_stat_t *p_statbuf =
(iso9660_stat_t *) _cdio_list_node_data (p_entnode);
iso9660_name_translate(p_statbuf->filename, filename);
len = strlen(filename);
if (!(2 == p_statbuf->type) ) {
if (! strcmp(filename + (len-3), "vob")) {
start = p_statbuf->lsn;
blocks = CEILING(p_statbuf->size, DVDCSS_BLOCK_SIZE);
start_map[start] = strdup(filename);
if (blocks == 0) {
//file length of 0 would result in a blocks of 0, and don't want
//to subtract one from it.
end_blocks[start] = start;
} else {
//-1 as start block is included in count of blocks
end_blocks[start] = start - 1 + blocks;
}
printf("%s: %llu->%llu (%llu blocks)\n", filename, start, end_blocks[start], blocks);
if (blocks) {
if (find(start_blocks.begin(), start_blocks.end(), start) == start_blocks.end()) {
start_blocks.push_back(start);
}
}
}
}
}
_cdio_list_free (p_entlist, true);
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
int flags = O_RDWR;
off64_t disc_len;
int total_blocks;
unsigned long long pos = 0;
iso9660_t * p_iso;
char * buffer;
unsigned long long preamble;
unsigned long long count = 0;
if (argc == 3 && !strcmp(argv[2], "-test")) {
flags = O_RDONLY;
}
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc < 2) {
printf("usage:\n\t %s <input iso>\n", argv[0]);
return 1;
}
if ((fd = open(argv[1], flags)) < 0) {
printf("failed to open input/output file\n");
return 1;
}
/* figure out how big the ISO image is */
if ((disc_len = lseek64(fd, 0, SEEK_END)) < 0) {
perror("lseek64 failed");
return 1;
}
total_blocks = disc_len / DVDCSS_BLOCK_SIZE;
if (disc_len != (long long) total_blocks * DVDCSS_BLOCK_SIZE) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_iso = iso9660_open(argv[1])) == NULL) {
printf("couldn't open %s as UDF\n", argv[1]);
return 1;
}
find_start_blocks(p_iso);
sort(start_blocks.begin(), start_blocks.end());
for(vector<unsigned long long>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %llu\n", *it);
}
for(map<unsigned long long, char *>::iterator p = start_map.begin(); p != start_map.end(); p++) {
printf("%s : %llu\n", p->second, p->first);
}
if (argc == 3 && !strcmp(argv[2], "-test")) {
return 0;
}
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
printf("dvdcss_open failed\n");
return 1;
}
/* if not scrambled skip! */
/* this doesn't do anything on iso input */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
lseek64(fd, preamble*DVDCSS_BLOCK_SIZE, SEEK_SET);
pos = preamble;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int cur = pos;
int end = start_blocks[0];
fflush(NULL);
start_blocks.erase(start_blocks.begin());
//printf("syncing at position = %llu, next sync point at %u\n", pos, end);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
//Q. Can it ever fail to get key here, but have the key
//elsewhere? perhaps have some accounting if this fails,
//know no point in trying to dvdcss_read() till next
//DVDCSS_SEEK_KEY?
printf("failed to seek to %llu: %s\n", pos, dvdcss_error(input));
}
while (pos < end) {
int read_size;
char * tmp_buffer;
int reseek;
fflush(NULL);
if (pos + len > end) {
len = end - pos;
}
read_size = len * DVDCSS_BLOCK_SIZE;
if ((blocks_read = read(fd, buffer, read_size)) != read_size) {
printf("short read, not handled yet\n");
return 1;
}
tmp_buffer = buffer;
reseek = 0;
for(int index = 0; index < len; index++) {
char block[2048];
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
int skip=0;
if (pos + index > end_blocks[cur]) {
printf("should skipping decode of supposed encrypted block (%llu) as not within VOB\n", pos+index);
bcopy(tmp_buffer, block, 2048);
skip=1;
}
count++;
if (dvdcss_seek(input, pos+index, DVDCSS_NOFLAGS) < 0) {
printf("failed to seek to %llu (index %d): %s\n", pos+index, index, dvdcss_error(input));
return 1;
}
if (dvdcss_read(input, tmp_buffer, 1, DVDCSS_READ_DECRYPT) != 1) {
printf("dvdcss_read failed\n");
return 1;
}
if (skip) {
printf("testing block we are skipping anyways\n");
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
printf("dvdcss \"decoded\" block still has bit set\n");
if (!memcmp(tmp_buffer, block, 2048)) {
printf("dvdcss \"decoded\" block didn't change!\n");
} else {
printf("dvdcss \"decoded\" block changed!\n");
}
} else {
printf("dvdcss \"decoded\" block got bit removed!\n");
}
} else {
lseek64(fd, (pos+index) * DVDCSS_BLOCK_SIZE, SEEK_SET);
if (my_write(fd, tmp_buffer, DVDCSS_BLOCK_SIZE) < 0) {
return 1;
}
reseek = 1;
}
}
tmp_buffer = tmp_buffer + DVDCSS_BLOCK_SIZE;
}
pos += len;
if (reseek) {
lseek64(fd, pos * DVDCSS_BLOCK_SIZE, SEEK_SET);
}
}
}
printf("descrambled %llu blocks\n", count);
printf("\n");
return 0;
}
SledgeHammer_999
31st October 2010, 15:53
Since this is C++ code you should change the includes to:
#define _FILE_OFFSET_BITS 64
#define _LARGEFILE64_SOURCE 1
#define _LARGEFILE_SOURCE 1
#include <cstdio>
#include <unistd.h>
#include <dvdcss/dvdcss.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cdio/cdio.h>
#include <cdio/iso9660.h>
#include <vector>
#include <map>
#include <algorithm>
#include <cstring>
#include <strings.h>
#include <cctype>
#include <cerrno>
using namespace std;
#define MAX 2000
#define CEILING(x, y) ((x+(y-1))/y) //ripped from libudf
vector<unsigned long long> start_blocks;
map<unsigned long long, unsigned long long> end_blocks;
map<unsigned long long, char *> start_map;
ssize_t my_write(int fd, const void * buf, size_t count)
{
int ret;
size_t my_count = count;
char * my_buf = (char *) buf;
while (my_count) {
if (my_count != count) {
printf("looped in my_write, last write = %d\n", ret);
}
if ((ret = write(fd, my_buf, my_count)) == -1) {
if (errno != EINTR) {
perror("write failed!");
goto out;
}
} else {
my_count -= ret;
my_buf += ret;
}
}
ret = count;
out:
return ret;
}
void find_start_blocks(iso9660_t *p_iso)
{
CdioList_t *p_entlist;
CdioListNode_t *p_entnode;
if (!p_iso)
return;
p_entlist = iso9660_ifs_readdir (p_iso, "/video_ts/");
if (p_entlist) {
_CDIO_LIST_FOREACH (p_entnode, p_entlist)
{
unsigned long long start;
unsigned long long blocks;
char filename[4096];
int len;
iso9660_stat_t *p_statbuf =
(iso9660_stat_t *) _cdio_list_node_data (p_entnode);
iso9660_name_translate(p_statbuf->filename, filename);
len = strlen(filename);
if (!(2 == p_statbuf->type) ) {
if (! strcmp(filename + (len-3), "vob")) {
start = p_statbuf->lsn;
blocks = CEILING(p_statbuf->size, DVDCSS_BLOCK_SIZE);
start_map[start] = strdup(filename);
if (blocks == 0) {
//file length of 0 would result in a blocks of 0, and don't want
//to subtract one from it.
end_blocks[start] = start;
} else {
//-1 as start block is included in count of blocks
end_blocks[start] = start - 1 + blocks;
}
printf("%s: %llu->%llu (%llu blocks)\n", filename, start, end_blocks[start], blocks);
if (blocks) {
if (find(start_blocks.begin(), start_blocks.end(), start) == start_blocks.end()) {
start_blocks.push_back(start);
}
}
}
}
}
_cdio_list_free (p_entlist, true);
}
}
int main(int argc, char *argv[])
{
dvdcss_t input;
int fd;
int flags = O_RDWR;
off64_t disc_len;
int total_blocks;
unsigned long long pos = 0;
iso9660_t * p_iso;
char * buffer;
unsigned long long preamble;
unsigned long long count = 0;
if (argc == 3 && !strcmp(argv[2], "-test")) {
flags = O_RDONLY;
}
if (!(buffer = (char *) malloc(MAX*DVDCSS_BLOCK_SIZE))) {
printf("failed to allocate space for buffer\n");
return 0;
}
if (argc < 2) {
printf("usage:\n\t %s <input iso>\n", argv[0]);
return 1;
}
if ((fd = open(argv[1], flags)) < 0) {
printf("failed to open input/output file\n");
return 1;
}
/* figure out how big the ISO image is */
if ((disc_len = lseek64(fd, 0, SEEK_END)) < 0) {
perror("lseek64 failed");
return 1;
}
total_blocks = disc_len / DVDCSS_BLOCK_SIZE;
if (disc_len != (long long) total_blocks * DVDCSS_BLOCK_SIZE) {
printf("partial block?????\n");
return 1;
}
printf("total_blocks = %d\n", total_blocks);
start_blocks.push_back(total_blocks);
/* find locations where have to rekey CSS */
if ((p_iso = iso9660_open(argv[1])) == NULL) {
printf("couldn't open %s as UDF\n", argv[1]);
return 1;
}
find_start_blocks(p_iso);
sort(start_blocks.begin(), start_blocks.end());
for(vector<unsigned long long>::iterator it = start_blocks.begin(); it != start_blocks.end(); it++) {
printf("end pos = %llu\n", *it);
}
for(map<unsigned long long, char *>::iterator p = start_map.begin(); p != start_map.end(); p++) {
printf("%s : %llu\n", p->second, p->first);
}
if (argc == 3 && !strcmp(argv[2], "-test")) {
return 0;
}
/* prep CSS */
if (!(input = dvdcss_open(argv[1]))) {
printf("dvdcss_open failed\n");
return 1;
}
/* if not scrambled skip! */
/* this doesn't do anything on iso input */
if (! dvdcss_is_scrambled(input)) {
printf("dvd isn't scrambled\n");
return 0;
}
preamble = start_blocks[0];
start_blocks.erase(start_blocks.begin());
lseek64(fd, preamble*DVDCSS_BLOCK_SIZE, SEEK_SET);
pos = preamble;
while (! start_blocks.empty()) {
int len = MAX;
int blocks_read;
int cur = pos;
int end = start_blocks[0];
fflush(NULL);
start_blocks.erase(start_blocks.begin());
//printf("syncing at position = %llu, next sync point at %u\n", pos, end);
if ( dvdcss_seek(input, pos, DVDCSS_SEEK_KEY) < 0) {
//Q. Can it ever fail to get key here, but have the key
//elsewhere? perhaps have some accounting if this fails,
//know no point in trying to dvdcss_read() till next
//DVDCSS_SEEK_KEY?
printf("failed to seek to %llu: %s\n", pos, dvdcss_error(input));
}
while (pos < end) {
int read_size;
char * tmp_buffer;
int reseek;
fflush(NULL);
if (pos + len > end) {
len = end - pos;
}
read_size = len * DVDCSS_BLOCK_SIZE;
if ((blocks_read = read(fd, buffer, read_size)) != read_size) {
printf("short read, not handled yet\n");
return 1;
}
tmp_buffer = buffer;
reseek = 0;
for(int index = 0; index < len; index++) {
char block[2048];
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
int skip=0;
if (pos + index > end_blocks[cur]) {
printf("should skipping decode of supposed encrypted block (%llu) as not within VOB\n", pos+index);
bcopy(tmp_buffer, block, 2048);
skip=1;
}
count++;
if (dvdcss_seek(input, pos+index, DVDCSS_NOFLAGS) < 0) {
printf("failed to seek to %llu (index %d): %s\n", pos+index, index, dvdcss_error(input));
return 1;
}
if (dvdcss_read(input, tmp_buffer, 1, DVDCSS_READ_DECRYPT) != 1) {
printf("dvdcss_read failed\n");
return 1;
}
if (skip) {
printf("testing block we are skipping anyways\n");
if( ((uint8_t*)tmp_buffer)[0x14] & 0x30 ) {
printf("dvdcss \"decoded\" block still has bit set\n");
if (!memcmp(tmp_buffer, block, 2048)) {
printf("dvdcss \"decoded\" block didn't change!\n");
} else {
printf("dvdcss \"decoded\" block changed!\n");
}
} else {
printf("dvdcss \"decoded\" block got bit removed!\n");
}
} else {
lseek64(fd, (pos+index) * DVDCSS_BLOCK_SIZE, SEEK_SET);
if (my_write(fd, tmp_buffer, DVDCSS_BLOCK_SIZE) < 0) {
return 1;
}
reseek = 1;
}
}
tmp_buffer = tmp_buffer + DVDCSS_BLOCK_SIZE;
}
pos += len;
if (reseek) {
lseek64(fd, pos * DVDCSS_BLOCK_SIZE, SEEK_SET);
}
}
}
printf("descrambled %llu blocks\n", count);
printf("\n");
return 0;
}
spotter
31st October 2010, 17:37
it's mostly a C program w/ C++ data structures algorithms. started off as a c program, but was lazy and didn't want to write my own map/vector data structures or depend on even more libs. so yea, could be benefit to change, but doesn't matter much (or teach me why I'm wrong).
SledgeHammer_999
31st October 2010, 19:10
Since you're using C++ features and you compile it using g++ it is considered a C++ program (although one can see that it started as a C program). Also C++, in order to remain compatible with C code, it introduced a new way to include the standard libs. As far as I know this is the standard way in C++ to include the "C standard libs". Maybe it is done to prevent compatibility issues, I am not sure.
spotter
31st October 2010, 19:41
ok, updated my version, seems to make no difference in binary produced by gcc, same exact size when stripped (unstripped, slightly larger w/ c++ headers).
qyot27
31st October 2010, 20:51
Now the error reads:
g++ `pkg-config --cflags --libs libdvdcss libiso9660` decrypter-v7.cpp
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xc0): undefined reference to `iso9660_ifs_readdir'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xd8): undefined reference to `_cdio_list_begin'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xeb): undefined reference to `_cdio_list_node_data'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x109): undefined reference to `iso9660_name_translate'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x2ce): undefined reference to `_cdio_list_node_next'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x2f3): undefined reference to `_cdio_list_free'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x52b): undefined reference to `iso9660_open'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x6f8): undefined reference to `dvdcss_open'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x733): undefined reference to `dvdcss_is_scrambled'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x86d): undefined reference to `dvdcss_seek'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0x887): undefined reference to `dvdcss_error'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xa66): undefined reference to `bcopy'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xaad): undefined reference to `dvdcss_seek'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xac3): undefined reference to `dvdcss_error'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccjjuOeD.o:decrypter-v7.cpp:(.text+0xb32): undefined reference to `dvdcss_read'
collect2: ld returned 1 exit status
The same occurs with the version SledgeHammer_999 posted as well. Even though I installed libdvdcss and libcdio to /mingw, and even tried /local and msys' root. All the same error. And the bcopy problem is still there. The issue at least partially stems from the fact MinGW doesn't define bcopy at all. Switching to memcpy and reversing the src/dest parameters (I'm guessing those are 'tmp_buffer' and 'block' in said call) resolves the bcopy error, but the libdvdcss and libcdio errors remain.
Is that stuff related to the code itself or to the parameters passed to g++? Up until now my compiling experience is with projects with full-fledged build systems, not calling the compiler directly.
spotter
31st October 2010, 21:17
assuming everything is installed in default lookup path, you would compile it like
g++ -ldvdcss -ludf -liso9660 -lcdio -lm <source code>
(though in reality the -ludf isn't needed for latest version as not using any udf calls)
for instance, on my ubuntu box, pkg-config says the params should be
-I/usr/include/libdvdcss -ldvdcss -ludf -liso9660 -lcdio -lm
i.e. all libs are stored in normal place and dvdcss headers are stored under /usr/include/libdvdcss as their rooted location.
now if the above command doesn't work, i.e. returns an error like
spotter@dent:~/dvdripper$ g++ -lfoobar 6.cpp
/usr/bin/ld: cannot find -lfoobar
collect2: ld returned 1 exit status
then that means it's not stored in the normal location, to specify search path for libraries you use
-L/path/to/lib
you can have multiple of these -L calls.
spotter
1st November 2010, 05:21
qyot27: any progress?
qyot27
1st November 2010, 17:23
Nope. I tried all of these different commands (as I went and explicitly duplicated the include/lib across all the main paths in the environment, just in case it wasn't picking them up in /local):
g++ -ldvdcss -ludf -liso9660 -lcdio -lm decrypter-v7.cpp
g++ -I/include -L/lib -ldvdcss -ludf -liso9660 -lcdio -lm decrypter-v7.cpp
g++ -I/mingw/include -L/mingw/lib -ldvdcss -ludf -liso9660 -lcdio -lm decrypter-v7.cpp
g++ -I/local/include -L/local/lib -ldvdcss -ludf -liso9660 -lcdio -lm decrypter-v7.cpp
All resulted in the same 'undefined reference' errors. By default, libcdio and libdvdcss installed themselves to /local, but using WinRAR I archived and then unpacked them to /mingw and / as well (as msys' / = /usr in a normal *nix setup). I even went and edited all of pkg-config's .pc files in /local/lib/pkgconfig to point to /local instead of /usr/local, but that didn't change anything (and I already knew msys understands /usr locations to be points in its root directory).
Based on the error messages, which talk about an .o file residing in the Temp folder in Local Settings, I went and watched said folder as I ran the command. No .o file gets generated there, although a .s file does. What's more, is that the filename given for the .o file and the generated .s file don't match either. Unfortunately, as soon as the command errors and exits, the .s file disappears. I'm not sure why MSys is calling a location completely outside of its filesystem tree, but I wonder if that's not a part of why this isn't working.
I did use the SVN and git versions of libdvdcss and libcdio respectively, might that be what's going wrong? On Ubuntu I simply used the stock/repo versions (libdvdcss-dev from Medibuntu), which are more likely than not from the release tarballs than the development branch. On Ubuntu it compiled fine, but I know from experience that things don't always go as smoothly with MSys as it does on a true Linux system.
SledgeHammer_999
1st November 2010, 18:00
qyote27, I understand that you know what you're doing, but I have to ask this. Did you compile libdvdcss and the other libs? Do the compiled objects exist in the paths that mingw searches? The warnings show that it fails on the linking process not on the compiling.
spotter
1st November 2010, 18:33
I spent a little time w/ mingw on a server 2003 box, failed to build both wget and libcdio. can't do much with it now.
qyot27
1st November 2010, 20:37
qyote27, I understand that you know what you're doing, but I have to ask this. Did you compile libdvdcss and the other libs? Do the compiled objects exist in the paths that mingw searches? The warnings show that it fails on the linking process not on the compiling.
Yes, I compiled both, straight from the SVN repo for libdvdcss and the git repo for libcdio, using the latest pre-prepared MSys/MinGW environment provided by the CCCP Wiki. I decided on that because it was a clean environment that others could use to try and replicate what I'm seeing, separated from my other setups.
The direct link to the environment is:
http://www.cccp-project.net/nichorai/msys.premade.2010.09.07.7z
I'll retrace everything I did here so it can be reviewed.
In a normal cmd.exe prompt:
svn checkout svn://svn.videolan.org/libdvdcss libdvdcss
git clone git://git.sv.gnu.org/libcdio.git
In MSys' rxvt prompt:
libcdio:
cd libcdio
./autogen.sh
make
make install
libdvdcss:
cd $HOME/libdvdcss/trunk
./bootstrap
./configure
make
make install
The readout of ls showing that the files are in /local:
Stephen@[computer-name-omitted] ~
$ cd /local
Stephen@[computer-name-omitted] /local
$ ls -R include
include:
cdio cdio++ dvdcss
include/cdio:
audio.h cdtext.h logging.h posix.h udf_file.h
bytesex.h device.h mmc.h read.h udf_time.h
bytesex_asm.h disc.h mmc_cmds.h rock.h utf8.h
cd_types.h ds.h mmc_hl_cmds.h sector.h util.h
cdda.h dvd.h mmc_ll_cmds.h track.h version.h
cdio.h ecma_167.h mmc_util.h types.h xa.h
cdio_config.h iso9660.h paranoia.h udf.h
include/cdio++:
cdio.hpp device.hpp disc.hpp iso9660.hpp read.hpp
cdtext.hpp devices.hpp enum.hpp mmc.hpp track.hpp
include/dvdcss:
dvdcss.h
Stephen@[computer-name-omitted] /local
$ ls -R lib
lib:
libcdio++.a libcdio_cdda.dll.a libdvdcss.la libudf.a
libcdio++.dll.a libcdio_cdda.la libiso9660++.a libudf.dll.a
libcdio++.la libcdio_paranoia.a libiso9660++.dll.a libudf.la
libcdio.a libcdio_paranoia.dll.a libiso9660++.la pkgconfig
libcdio.dll.a libcdio_paranoia.la libiso9660.a
libcdio.la libdvdcss.a libiso9660.dll.a
libcdio_cdda.a libdvdcss.dll.a libiso9660.la
lib/pkgconfig:
libcdio++.pc libcdio_cdda.pc libdvdcss.pc libiso9660.pc
libcdio.pc libcdio_paranoia.pc libiso9660++.pc libudf.pc
Stephen@[computer-name-omitted] /local
$
The $PATH variable area in /etc/profile:
if [ $MSYSTEM == MINGW32 ]; then
export PATH=".:/usr/local/bin:/mingw/bin:/bin:$PATH"
else
export PATH=".:/usr/local/bin:/bin:/mingw/bin:$PATH"
fi
Using 'echo $PATH' shows those three directories, plus all my Windows PATH settings after them (separated on new lines for ease of reading):
.:/usr/local/bin
:/mingw/bin
:/bin
:/c/Program Files/Microsoft Visual Studio 9.0/Common7/IDE
:/c/Program Files/Microsoft Visual Studio 9.0/VC/BIN
:/c/Program Files/Microsoft Visual Studio 9.0/Common7/Tools
:/c/WINDOWS/Microsoft.NET/Framework/v3.5
:/c/WINDOWS/Microsoft.NET/Framework/v2.0.50727
:/c/Program Files/Microsoft Visual Studio 9.0/VC/VCPackages
:/c/Program Files/Microsoft SDKs/Windows/v6.0A/bin
:/c/Program Files/ImageMagick-6.5.5-Q16
:/c/WINDOWS/system32
:/c/WINDOWS
:/c/WINDOWS/system32/wbem
:/c/Program Files/cvsnt
:/c/Program Files/oggz-tools-0.9.9
:/c/Program Files/megui/tools/eac3to
:/c/Program Files/dvda-author
:/c/Program Files/FLAC
:/c/Program Files/MediaInfo
:/c/Program Files/megui/tools/xvid_encraw
:/c/Program Files/mplayer
:/c/Program Files/DGPulldown
:/c/Program Files/VirtualDubMod
:/c/Program Files/iTunesEncode
:/c/Program Files/megui/tools/x264
:/c/Program Files/HC025
:/c/Program Files/megui/tools/ffmpeg
:/c/Program Files/MKVtoolnix
:/c/Program Files/SVN-cygwin
:/c/Program Files/GTK2-Runtime/bin
:/c/Program Files/Microsoft SQL Server/90/Tools/binn
:/c/Program Files/QuickTime/QTSystem
:/c/Program Files/Microsoft SQL Server/90/Tools/binn/
:/c/Program Files/AviSynth 2.5/plugins/
:/c/Program Files/Windows NT/Accessories/
:/c/Program Files/dvdauthor
:/c/Program Files/Git/bin
:/c/Program Files/LAME
:/c/Program Files/qemu
:/c/Program Files/toolame-02l
:/c/WINDOWS/Python26
:/c/Program Files/SSRC
:/c/Program Files/rtmpdump
:/c/Program Files/mp4muxer
Leaving the install at the default location (/local) and attempting to compile the decrypter fails with an explicit warning about not finding dvdcss.h.
g++ -ldvdcss -ludf -liso9660 -lcdio -lm decrypter-v7.cpp
decrypter-v7.cpp:7:27: fatal error: dvdcss/dvdcss.h: No such file or directory
compilation terminated.
If I specify -I and -L parameters to identify that they are in /local:
g++ -I/local/include -L/local/lib -ldvdcss -ludf -liso9660 -lcdio -lm decrypter-v7.cpp
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0xc0): undefined reference to `iso9660_ifs_readdir'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0xd8): undefined reference to `_cdio_list_begin'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0xeb): undefined reference to `_cdio_list_node_data'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x109): undefined reference to `iso9660_name_translate'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x2ce): undefined reference to `_cdio_list_node_next'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x2f3): undefined reference to `_cdio_list_free'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x52b): undefined reference to `iso9660_open'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x6f8): undefined reference to `dvdcss_open'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x733): undefined reference to `dvdcss_is_scrambled'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x86d): undefined reference to `dvdcss_seek'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0x887): undefined reference to `dvdcss_error'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0xaad): undefined reference to `dvdcss_seek'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0xac3): undefined reference to `dvdcss_error'
C:\DOCUME~1\Stephen\LOCALS~1\Temp\ccuqFOh5.o:decrypter-v7.cpp:(.text+0xb32): undefined reference to `dvdcss_read'
collect2: ld returned 1 exit status
The linking errors are why I was wondering if the fact I was using the development branches of libdvdcss and libcdio could be causing the problem - APIs changing and whatforth, calls being abandoned, etc.
spotter
1st November 2010, 21:05
I really doubt the api has changed much, especially w/ libdvdcss its difficult for it to change as applications generally dynamically load it if its availalable and ignore it if not.
on my linux box, I can do
spotter@dent:/usr/lib$ nm libdvdcss.a
to list all symbols
also
spotter@dent:/usr/lib$ nm -D libdvdcss.so
will do a similar thing on the dynamic version
with that said, playing around a bit, on my box
spotter@dent:~/dvdripper$ g++ -static `pkg-config --cflags --libs libdvdcss libudf libiso9660` 6.cpp
/tmp/ccTIc0df.o: In function `find_start_blocks(_iso9660_s*)':
6.cpp:(.text+0xd1): undefined reference to `iso9660_ifs_readdir'
6.cpp:(.text+0xf2): undefined reference to `_cdio_list_begin'
6.cpp:(.text+0x10b): undefined reference to `_cdio_list_node_data'
6.cpp:(.text+0x12f): undefined reference to `iso9660_name_translate'
6.cpp:(.text+0x394): undefined reference to `_cdio_list_node_next'
6.cpp:(.text+0x3c2): undefined reference to `_cdio_list_free'
/tmp/ccTIc0df.o: In function `main':
6.cpp:(.text+0x648): undefined reference to `iso9660_open'
6.cpp:(.text+0x867): undefined reference to `dvdcss_open'
6.cpp:(.text+0x89f): undefined reference to `dvdcss_is_scrambled'
6.cpp:(.text+0xa10): undefined reference to `dvdcss_seek'
6.cpp:(.text+0xa29): undefined reference to `dvdcss_error'
6.cpp:(.text+0xc41): undefined reference to `dvdcss_seek'
6.cpp:(.text+0xc56): undefined reference to `dvdcss_error'
6.cpp:(.text+0xcc3): undefined reference to `dvdcss_read'
collect2: ld returned 1 exit status
i.e. same error. doesn't want to compile statically.
unsure what the problem is.
spotter
1st November 2010, 21:24
from irc
"spotter: your libraries should be listed after your source file and in order of dependency"
try
g++ 6.cpp -I/usr/include/libdvdcss -ldvdcss -ludf -liso9660 -lcdio -lm
works w/ --static for me now.
qyot27
1st November 2010, 23:23
Thanks, got it figured out now.
With that pre-made environment (and the instructions I laid about about how I got libdvdcss and libcdio compiled):
g++ -static decrypter-v7.cpp -I/local/include -L/local/lib -ldvdcss -ludf -liso9660 -lcdio -lm -liconv
Compiles successfully. Now I have to see about getting lsdvd compiled. Or at least find an equivalent for Windows.
spotter
1st November 2010, 23:53
I bet if you run vlc to play the disk it will get authenticated as well. (I sometimes did that for disc's lsdvd would fail on)
if ddrescue throws lots of fits of unreadable sectors (basically never gets up to speed, because keeps hitting bad sectors) then it wasn't authenticated, but if it reads w/o a problem (most TV shows for instance don't have unreadable sectors I've found besides for abc shows), then you can drag/drop the iso onto vlc (vlc will work fine w/ the scrambled sectors iso). my program can then be run on it to descramble it and try it w/ a mounting program.
qyot27
2nd November 2010, 00:45
That actually requires playing a title in VLC, though, right?
I'm really not sure how ddrescue is supposed to interact, actually. After loading the disc with VLC (and even playing a title), running ddrescue with the command earlier, amended slightly for Windows (ddrescue -v -b 2048 -d E:/ image.iso image.log), it registers in the Petabytes and ipos and opos cycle without writing anything. I have to stop it prematurely.
ddrescue -v -b 2048 -d E:/ image.iso
cygwin warning:
MS-DOS style path detected: E:/
Preferred POSIX equivalent is: /cygdrive/e
CYGWIN environment variable option "nodosfilewarning" turns off this warning.
Consult the user's guide for more details about POSIX paths:
http://cygwin.com/cygwin-ug-net/using.html#using-pathnames
About to copy 9223 PBytes from E:/ to image.iso
Starting positions: infile = 0 B, outfile = 0 B
Copy block size: 32 hard blocks
Hard block size: 2048 bytes
Max_retries: 0
Direct: yes Sparse: no Split: yes Truncate: no
Press Ctrl-C to interrupt
rescued: 0 B, errsize: 9223 PB, current rate: 0 B/s
ipos: 68715 MB, errors: 1, average rate: 0 B/s
opos: 68715 MB, time from last successful read: 23 s
Splitting failed blocks...
Interrupted by user
The disc in question (Toradora! Volume 1 Disc 1, replacement) isn't one with any weird obfuscation techniques or damaged sectors. MPC and SmartRipper both recognize it as having CSS, though.
spotter
2nd November 2010, 01:26
1) I'd guess you don't have to play a title as usually even the menus have css scrambled sectors.
2) e: is a file system to cygwin. need to be the raw block device.
from http://www.cygwin.com/cygwin-ug-net/using-specialnames.html
it appears as if it be /dev/sr0,1....
qyot27
2nd November 2010, 04:06
Ok, /dev/scd1 appears to work. It's copying now so I'll see how all this turns out after it's done.
Does VLC have to remain open during the ddrescue operation, or is that safe to close after it sees the disc?
spotter
2nd November 2010, 04:10
good luck, I'd make a copy of the ISO with scrambled sectors before you do anything with it, as if my program is buggy, the ddrescue part is very time consuming so a pain to have to redo.
qyot27
2nd November 2010, 04:54
Well, didn't actually seem to take much longer than an average disc read, just 17min 15sec if I go by the created and modified timestamps on the ISO. On this computer I'm accustomed to long wait times anyway.
In any case, the decrypter gave an error this time (the output from ddrescue really is named 'image.iso'):
C:\>isodecrypter.exe image.iso
failed to open input/output file
C:\>
The .exe and .iso are in the same folder, so it should be seeing it (and specifying the full path to the .iso gave the same error). I don't think there's anything the matter with the ISO, as VLC can read it sans-scrambling, while MPC and MPC-HC clearly show that it is still there in the stream.
spotter
2nd November 2010, 04:59
hmm. let me add some more debugging output.
replace the printf with a perror
perror("failed to open input/output file");
that should give a printout on why open() failed.
spotter
2nd November 2010, 05:42
the code should be fairly straight forward, you can try playing around w/ a simple C program to try and open the file to see what goes wrong. my only possible guess at this point, spaces in the filename? edit: though obviously incorrect as your example shows.
spotter
2nd November 2010, 16:22
building it myself with mingw. first issue, last release of libcdio doesn't work with it, using git repo.
spotter
2nd November 2010, 16:58
pretty sure now it has to do with the large size of the file.
spotter
2nd November 2010, 17:11
100% positive this problem has to do w/ large file size, seems to be an issue w/ mingw see lots of other references for issues with files 4GB or larger.
tried it on an < 4GB iso and it "worked" (i.e. got passed open() call), but failed do to me detecting a short read and not handling that situation yet. never had that occur on linux will play with it more tonight.
spotter
2nd November 2010, 19:07
assumption is the problem is in my code, as both libcdio's and dvdcss's open calls work fine.
JohnAStebbins
2nd November 2010, 19:29
mingw doesn't support large file sizes well. HandBrake has been using the following patch to make libdvdread work with large iso images.
diff -Naur libdvdread.orig/src/dvd_input.h libdvdread/src/dvd_input.h
--- libdvdread.orig/src/dvd_input.h 2008-10-03 13:11:30.000000000 -0700
+++ libdvdread/src/dvd_input.h 2009-04-23 13:47:04.000000000 -0700
@@ -29,6 +29,24 @@
#define DVDINPUT_READ_DECRYPT (1 << 0)
+#if defined( __MINGW32__ )
+# undef lseek
+# define lseek _lseeki64
+# undef fseeko
+# define fseeko fseeko64
+# undef ftello
+# define ftello ftello64
+# define flockfile(...)
+# define funlockfile(...)
+# define getc_unlocked getc
+# undef off_t
+# define off_t off64_t
+# undef stat
+# define stat _stati64
+# define fstat _fstati64
+# define wstat _wstati64
+#endif
+
typedef struct dvd_input_s *dvd_input_t;
/**
spotter
2nd November 2010, 19:40
don't see why any of that would impact open() actually failing. what I do on the fd after the fact, yea, but on the function itself?
I'm really confused. libcdio and dvdcss work fine and I do (from what I can tell) the exasct same thing. I wish I had strace available.
spotter
2nd November 2010, 19:59
so I stuck in a spotter_open into libdvdcss
int spotter_open(char const *file)
{
return open(file, O_RDWR | O_BINARY);
}
and this seemed to get passed that problem.
but not a real solution.
qyot27
2nd November 2010, 20:02
Speaking of libdvdread, I ran into a roadblock on it when trying to get it compiled, because of 'libdl' and a 'dlopen' call not being present. I couldn't go any further than that, which was why I had to settle on using VLC for the drive authentication instead of continuing to try building lsdvd. A Google search seemed to point to those being a part of libc6 that handles dynamic linking, so I would guess the Handbrake patch for large file support wouldn't matter on that point?
And yeah, the ISO I was testing earlier was from a dual layer, a full 7.47GB. I'm not sure if I have any DVDs with content under 4GB that have encryption on them. The ones I know of under that size are discs I've authored or manipulated myself, and thus would lack encryption from the start.
EDIT: Where exactly should the spotter_open call be inserted in libdvdcss' source?
spotter
2nd November 2010, 20:05
hack libdvdcss to get it to work (working right now for me)
what I did. added to src/device.c the function I wrote before the libc_open() function
then in src/dvdcss/dvdcss.h added a prototype for it before the dvdcss_open() prototype.
then edit my program's call from open(file, open mode) to spotter_open(file)
and running now on a dvd of mine in windows with DVDCSS_VERBOSE=2
qyot27
2nd November 2010, 20:32
What's the actual prototype line to add in dvdcss.h? I have zero programming experience to know that sort of thing intuitively. The previous times I mentioned things like that were mostly by context of the references to it I could find from a search, but I can't see the context on that to know what it should look like (aside from copying the dvdcss_open prototype and replacing the name).
And I'm guessing open(file, open mode) is the if ((fd = open(argv[1], flags)) < 0) { line? So should that be
if ((fd = spotter_open(argv[1], flags)) < 0) {
or
if ((fd = spotter_open(argv[1])) < 0) {
?
spotter
2nd November 2010, 21:00
what I added (and some context to see where I added it). I'll note, this is a total hack. don't learn anything from this approach. :)
src/device.c
/* Following functions are local */
int spotter_open(char const * file)
{
return open(file, O_RDWR | O_BINARY);
}
/*****************************************************************************
* Open commands.
*****************************************************************************/
static int libc_open ( dvdcss_t dvdcss, char const *psz_device )
{
src/dvdcss/dvdcss.h
LIBDVDCSS_EXPORT dvdcss_t dvdcss_open ( char *psz_target );
LIBDVDCSS_EXPORT int spotter_open(char const * file);
LIBDVDCSS_EXPORT int dvdcss_close ( dvdcss_t );
my code
printf("argv1 = %s\n", argv[1]);
if ((fd = spotter_open(argv[1])) < 0) {
perror("failed to open input/output file");
return 1;
}
qyot27
2nd November 2010, 21:26
I recompiled libdvdcss and the decrypter and it seems to be working now, it recognized the ISO and there's definitely work occurring, as indicated by the orange light on my tower. We'll see how it turns out.
EDIT: The task finished, and MPC now detects no scrambling. It worked. Although the previous point about it not being a troublesome disc in the first place is still true.
spotter
3rd November 2010, 04:54
Playing around w/ imaging via imgburn. it complains about it being a scrambled disc (which it is), unsure if it does authentication or not, but authenticated via vlc. imgburn made a scrambled disc (like ddrescue) + an MDS file and my utility descrambled it. Not a troublesome disc with bad sectors.
spotter
3rd November 2010, 06:04
I recompiled libdvdcss and the decrypter and it seems to be working now, it recognized the ISO and there's definitely work occurring, as indicated by the orange light on my tower. We'll see how it turns out.
EDIT: The task finished, and MPC now detects no scrambling. It worked. Although the previous point about it not being a troublesome disc in the first place is still true.
my experience says you won't have any different experience w/ more troublesom discs.
The main way to attack this would be to confuse my "naive" method of seeing where VOBs start and stop to make me "descramble" sectors that shouldn't be touched or not unscramble sectors that need to be. I just doubt there are any discs that do that (though for all I know, I just gave them the idea)
JohnAStebbins
3rd November 2010, 17:55
Speaking of libdvdread, I ran into a roadblock on it when trying to get it compiled, because of 'libdl' and a 'dlopen' call not being present. I couldn't go any further than that, which was why I had to settle on using VLC for the drive authentication instead of continuing to try building lsdvd. A Google search seemed to point to those being a part of libc6 that handles dynamic linking, so I would guess the Handbrake patch for large file support wouldn't matter on that point?
And yeah, the ISO I was testing earlier was from a dual layer, a full 7.47GB. I'm not sure if I have any DVDs with content under 4GB that have encryption on them. The ones I know of under that size are discs I've authored or manipulated myself, and thus would lack encryption from the start.
EDIT: Where exactly should the spotter_open call be inserted in libdvdcss' source?
libdvdread has an internal dlopen compatibility function it will use if configure detects that the build environment doesn't have dlfcn.h. It only checks for the existence of this header file and not for the actual library libdl. So my guess is that you have this header file and not the library for some reason.
qyot27
3rd November 2010, 23:16
libdvdread has an internal dlopen compatibility function it will use if configure detects that the build environment doesn't have dlfcn.h. It only checks for the existence of this header file and not for the actual library libdl. So my guess is that you have this header file and not the library for some reason.
Checking over my /include directories, it didn't have dlfcn.h, nor the library.
However, there is a Google Code project that supplies them:
http://code.google.com/p/dlfcn-win32/
Of course, it seems there's a problem with libdvdread needing older versions of libtool and autoconf than those the environment came with.
spotter
7th November 2010, 08:12
wondering if anyone else has tried this? I've now made multiple rips by doing this
1) start dvd w/ VLC to authenticate disk
2) rip disk w/ scrambled sectors with imgburn (it will complain about css scrambled sectors, haven't really tried it with discs with invalid sectors, probably have to use it in a mode where it just ignores invalid sectors)
3) descramble sectors using my program.
seems to work great. I may be naive about this, but I really think the only way to defeat my program's naive approach would be to interleave real IFO data with fake VOB data such that when I try to descramble a supposed scrambled VOB block, I'm really corrupting a valid IFO block.
qyot27
7th November 2010, 17:56
I used ImgBurn to do some testing also, but like you said, not with any discs with bad sectors. I don't know how its Read abilities would fare against such discs, some elaboration on that would be needed (I do have a copy of Leap Year, though, so I can test that...apparently our standalone DVD players have trouble playing it too). I did, however, find out that once VLC gets the authentication step done, it doesn't need to remain open for the rescue operation to work.
If it turns out ImgBurn can't cope with bad sectors, at least there is ddrescue to fall back on. Non-Cygwin users can still grab that package from Cygwin's FTP repository and use it sans the full Cygwin environment, though (which is exactly what I did). The ddrescue package is here (ftp://ftp.gtlib.gatech.edu/pub/cygwin/release/ddrescue/), while the Cygwin system dependency package is here (ftp://ftp.gtlib.gatech.edu/pub/cygwin/release/cygwin/). Unpack both of them to C:\WINDOWS\cygwin or something, and then add C:\WINDOWS\cygwin\usr\bin to Windows' PATH variable (which can be done through Control Panel->System, regedit, or even a third-party tool (http://lingo.atspace.com/pathed.html)).
EDIT 11/11/10: Ah, seems that I was a tad mistaken about what's required for ddrescue to work. Some regular support dlls are necessary too (cyggcc, for instance). The reason I didn't realize this before is that those dlls were already in my PATH because they were sitting in C:\Program Files\Subversion. As I took that SVN installation from a Cygwin environment, I'd carried some of those dlls over already.
spotter
7th November 2010, 18:02
I used ImgBurn to do some testing also, but like you said, not with any discs with bad sectors. I don't know how its Read abilities would fare against such discs, some elaboration on that would be needed (I do have a copy of Leap Year, though, so I can test that...apparently our standalone DVD players have trouble playing it too). I did, however, find out that once VLC gets the authentication step done, it doesn't need to remain open for the rescue operation to work.
yea, same really with lsdvd, authentication only gets removed when disc is ejected.
If it turns out ImgBurn can't cope with bad sectors, at least there is ddrescue to fall back on. Non-Cygwin users can still grab that package from Cygwin's FTP repository and use it sans the full Cygwin environment, though (which is exactly what I did). The ddrescue package is here (ftp://ftp.gtlib.gatech.edu/pub/cygwin/release/ddrescue/), while the Cygwin system dependency package is here (ftp://ftp.gtlib.gatech.edu/pub/cygwin/release/cygwin/). Unpack both of them to C:\WINDOWS\cygwin or something, and then add C:\WINDOWS\cygwin\usr\bin to Windows' PATH variable (which can be done through Control Panel->System, regedit, or even a third-party tool (http://lingo.atspace.com/pathed.html)).
the main advantage of the imgburn approach is that its layer aware and therefore on burning of the css descrambled disc wont have to guess where the layer break should be, though from my limited experience its also able to do a very good job guessing where it should be.
also: my video card (8800GT) just went kaput over the weekend (have a 7900GS to fall back on, but not putting computer back in place till I get a replacement), so wont be doing much windows based testing. pretty comfortable with its ability in linux, though until its used heavily, wont trust that there are no bugs in it.
spotter
11th November 2010, 05:21
so I just tried (/in process for imgburn, but passed the bad parts) a DVD with invalid sectors with both ddrescue and imgburn. By telling imgburn to not retry invalid sectors, it had read errors on the exact same locations, and I'll do an md5sum on both images when imgburn is done, but my guess is that imgburn can be used to create the images as well. the main negative is that if you do have a scratched DVD, imgburn can't fill in the blanks from a different DVD without the scratches in those locations (very useful for recovering DVD images, which is how this project started in the first place)
spotter
11th November 2010, 05:40
same md5sum.
boykillsworld
16th November 2010, 02:31
Am I looking at this wrong because I generally just use
mplayer /dev/sr0 -dumpstream -dumpfile ${DVD_NAME}.iso
I've ripped about 120 DVD with this. I had two or three that didn't work so just used dvdfab in wine. Granted this is just what I feed handbrake with so I haven't checked the functionality of the iso. Vobcopy is terrible as I used to use it in my script. If your program really can get them all this would be a nice replacement but I don't think ddrescue works with all DVD. I often find that sometimes I am forced to just grab the movie with dvdfab as nothing in linux can grab the image.
spotter
16th November 2010, 02:37
why do you think ddrescue would fail? ddrescue will read all blocks of data that linux can read, if linux can't read it and it's needed to play the DVD, then it wont be playable correctly. Or let me rephrase, I'm more convinced about the ddrescue part of my methodology than my own program.
boykillsworld
16th November 2010, 05:09
Well i had it fail once at first I thought it was copy protection but I think it was the scratch on the DVD. Although it didn't necessarily fail I just canceled it after it took 3 hours and was making my drive make strange noises. I also disliked the approach because until now it did not remove the drm. I could pull the movie itself off but I could never make an image. It's probably a one off but if you try to rip an entire DVD the likelihood of a damaged section or being tripped up with a copy protection is substantially increased as supposed to just the movie itself. I do think the movie studios are running out of tricks but I've been trying forever to find a completely reliable way to copy just the movie on linux.
Keep up the good work as this may be the most reliable approach I've seen yet but the basis of a clean readable DVD may not be a reality.
spotter
16th November 2010, 05:26
Well, my approach would work with a dirty DVD as well and be no worse than any other approach, i.e. we'll just skip blocks that are scratched and can't be read and so will any other approach. The main reason I use ddrescue is that I was coming from a position where I was trying to rescue scratched DVDs so model was to rescue as much as I could and then borrow library DVD to fill in the blanks, as don't need library DVD for a lot to fill in the blanks, a pretty quick process.
setarip_old
17th November 2010, 08:20
@spotter
It looks like "The Last Airbender" would be an ideal DVD to test with your ripping tool...
spotter
17th November 2010, 08:28
I assume you mean the movie, not the TV show? might take awhile to get, 500+ holds on it in the library (though it looks like they might have ordered 300+ copies, so might cycle fast), I'll put it on hold as soon as a spot in my queue frees up, presumambly tomorrow.
xenex
17th November 2010, 08:39
So, since everyone seems to have problems with "The Last Airbender" with its 'latest and greatest' protection, I decided to give it a go with spotter's method. Complete success. In Linux I used ddrescue to create the ISO, which took 15 minutes for this disc, and then used spotter's program on the ISO, which took around 5 minutes. I then mounted the ISO in Windows XP using 'Virtual Clone Drive', and it played exactly like the original DVD including all menus, special features, previews, etc. etc.
I expected it would work. I have to agree with spotter, this approach is pretty close to "unbeatable." But it is because it is such a "naive" or "simple" technique that it works so well.
These "advanced" protections like ARccOS, RipGuard, etc. really can't do much to stop this method. They basically do 3 things beyond the CSS encryption:
Thing 1: Bad / Corrupt / Unreadable Sectors
"ddrescue" covers this. It simply reads all of the readable sectors from the disc, 'blanks' the unreadable ones, and creates an ISO file of the entire disc. The IFO files on the DVD instruct a compliant DVD player to skip the bad sectors, so they make no difference.
Thing 2: Messed up UDF filesystem
spotter's program addresses this by simply treating the DVD as a block device. It doesn't look at, parse, or address the filesystem in any way. It doesn't try to "fix it" or correct it. (Although I think it could, but that's for another post). Since compliant DVD players don't use or parse the filesystem either, the messed up filesystem makes no difference. The output ISO from spotter's program will still have the same messedupiness as the original disc, but for playback purposes, it makes no difference.
Thing 3: Screwy VTS's / Tiny Cells / Empty Cells / Etc.
These all affect rippers that try to parse/follow the IFO's in order to recreate the DVD in "file mode" as a VIDEO_TS folder. spotter's approach doesn't bother with that, it just simply copies the readable data from the DVD as a block device and decrypts those blocks that are CSS encrypted. So, if the DVD will play on a compliant DVD player (either Stand-Alone or in Software) then the ISO created by spotter's approach will also. QED.
This has the potential to be put together as a very nice Linux DVD Ripper package that defeats all known protections. And I have some ideas on how to do that as well. I shall post more on that later.
spotter
17th November 2010, 16:17
I should note that I do read the file system a little (though as ISO9660), in the sense that I look for beginning and end of VOBs as don't want to "decrypt" non VOB data. So I do think my approach is "Attackable" (interleave fake VOBs with real IFOs), but my guess is that this could mess up existing players, and why it hasn't been done.
tldr: I'm very naive in determining what sectors can be CSS encrypted and that could be a problem, would need more investigation if a problem actually came up.
xenex
18th November 2010, 12:18
spotter-
My previous post was not meant for you so much as it was meant for those who do not understand why your approach avoids/defeats the "advanced: - LOL - new DVD protections.
If, by posting TLDR you meant my post was "Too Long, Didn't Read." Well, sorry. sometimes it takes more than a 2 second soundbite to explain things.
I have already written C code that recognizes the DVD drive, resets the AGIDs, authenticates the drive and gathers the DVD title and other information. I also know how to use this approach to use a "ddrescue" approach to read an entire DVD and fix up the UDF filesystem.
I've already coded a lot of this for my own use. If you want to do things your own way, that's fine as well. I had hoped to work with you on this. When there is a fork in the road, take it, I guess.
I really thought you would be more open to working together to make an open-source program, but what do I know.
spotter
18th November 2010, 15:04
you misunderstood me. I was tldr'ing my own comment. summarising in plainer english. I'd have no problem working with you.
spotter
18th November 2010, 18:15
xenex: an interesting idea I had would be the distribution of map files that basically instruct the reader what sectors to skip for each DVD. (example, dealing with kick ass right now that has a boat load of invalid sectors, which makes reading very very long, though could be that my DVD is bad, but wont really know until I get another one to try and fill the holes with ddrescue)
spotter
18th November 2010, 20:46
actually guessing that kick ass DVD that I have is a defective, time to get another copy to verify.
Grungefuttock
21st November 2010, 18:25
Hi Guys
I have been following this thread from the beginning and it began to dawn on me that this is how DVDShrink started. I remember the first post from DVDShink when he introduced the early version of his program (I assume a male) and how other forum members contributed to his program. The rest is history and I still use Shrink at least a couple of times a week (via WINE as I am a Linux user and have been for the last six years).
If Spotter's program can progress along the same lines then I for one will be extremely grateful for all his and the other forum member contributions to making a Linux native ripping program that can handle all the encryptions that the DVD publishers are throwing at us.
Good luck to all
Grungefuttock
xenex
22nd November 2010, 08:53
@spotter
So, I've been looking for bugs, and trying to find a disc where your program "fails." Haven't found one yet. But I did find an "issue" - and it's not in your code - but in libdvdcss.
It seems that libdvdcss will (in rare cases) fail to crack the key for some (usually small) VOB files. In my case, I tried the recent movie "Predators" and it DOES NOT decrypt VTS_01_1.VOB.
Now, that VOB is nothing but 1 second of black, and it doesn't matter, but it might matter if it fails on other DVDs. I have a solution, though, but am still working on it. I'm not using libdvdcss at all, but am using the CSS implementation from the old (1999) css-auth code by Derek Fawcus.
If you are able, please try the "Predators" movie and see if you have same result. The R1 disc I tested is a totally standard DVD, no bad sectors, no weird protections, but it does have a key that libdvdcss won't crack.
spotter
22nd November 2010, 18:44
not all VOBs are encrypted. With that said, it could be how I use libdvdcss (who says I'm using it correctly).
i.e. I do a dvdcss_seek() to the start of each VOB, its possible (I'm guessing), that there will be no encrypted blocks for it to crack from the start to however far it checks and therefore the css key that it will use for later dvdcss_read() calls will be blank (or wrong). A possibility would be if that happens to do continue doing normal read() and check for css scrambled blocks and if one finds one in a vob that seek() failed on to reseek to that block's location and see if dvdcss can then crack the key.
so how I would test it,
1) if dvdcss_seek() fails, note it (code really already does it, but make it a boolean variable)
2) when the variable is set and one comes across an encrypted block (bit compare after regular read()), try to dvdcss_seek() to that location.
If it still doesn't work, then I'd say we should look at and see if vlc, totem.... have any issues with those VOBs, if they don't, its indicative that I was doing something wrong.
spotter
23rd November 2010, 09:25
ok, I now know how to calculate layer break position on the DVD
don't quite understand this code (namely why it produces the same output for layer 0 and 1), but it provides the info to calculate the same LB proposition as imgburn
#include <sys/ioctl.h>
#include <linux/cdrom.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <strings.h>
int main()
{
dvd_struct s;
int layer = 0;
int fd;
if ( (fd = open("/dev/dvd", O_RDONLY)) == -1) {
perror("failed to open /dev/dvd");
}
while (layer < 2) {
s.type = DVD_STRUCT_PHYSICAL;
s.physical.layer_num = layer;
if ( ioctl(fd, DVD_READ_STRUCT, &s) == -1) {
perror("ioctl failed");
}
printf("layer = %d\nbook_version = %d, book_type = %d, min_rate = %d, disc_size = %d, layer_type = %d, track_path = %d, nlayers = %d, track_density = %d, linear_density = %d, bca = %d, start_sector = %u, end_sector = %u, end_sector_l0 = %u\n\n", layer, s.physical.layer[layer].book_version, s.physical.layer[layer].book_type, s.physical.layer[layer].min_rate, s.physical.layer[layer].disc_size, s.physical.layer[layer].layer_type, s.physical.layer[layer].track_path, s.physical.layer[layer].nlayers, s.physical.layer[layer].track_density, s.physical.layer[layer].linear_density, s.physical.layer[layer].bca, s.physical.layer[layer].start_sector, s.physical.layer[layer].end_sector, s.physical.layer[layer].end_sector_l0);
bzero(&s, sizeof(dvd_struct));
layer++;
}
}
layer break = end_sector_l0 - start_sector + 1
xenex
23rd November 2010, 10:34
I don't understand why you are so concerned over the Layer Break. Any writable DVD medium cannot contain as much data (per layer) as a pressed DVD, so for "DVD burning" purposes the layer break will have to be re-calculated anyway.
Here is some output from the code I've been hacking at:
xenex@MBOXL:~/Desktop/css-auth xenex$ ./tstdvd /dev/sr1
Reset of AGIDs on drive /dev/sr1 successful
This DVD is CSS encrypted
Book Version: 1
Book Type: DVD-ROM
Disc Size: 120 mm
Num Layers: 2
Track Path: OTP
Layer 1 of [2]
Min Rate: 10.08 Mbits/sec
Layer Type: 0x1
Track Density: 0.74 um/track
Linear Density: 0.293 um/bit
BCA: Present
Start Sector 0x30000
End Sector 0xfc28cf
End Sector L0 0x20ae3f
Layer 2 of [2]
Min Rate: 10.08 Mbits/sec
Layer Type: 0x1
Track Density: 0.74 um/track
Linear Density: 0.293 um/bit
BCA: Present
Start Sector 0x30000
End Sector 0xfc28cf
End Sector L0 0x20ae3f
Request AGID [1]... AGID 3
Host sending challenge: 09 08 07 06 05 04 03 02 01 00
LU sent key1: FD 14 D4 74 2A
Drive Authentic - using variant 0
LU sent challenge: 04 6C 04 6C EB FF EB FF 1C 57
Host sending key 2: 0E 13 45 F2 EB
DVD is authenticated
Received Session Key: F4 87 62 61 5A
Received Disc Key: 53 24 5A F4 76 73 7A 22 B6 1B
Drive Authenticated
(I am FAR from finished with this. Much work to be done!)
End Sector L0 0x20ae3f tells where the data ends on Layer 1. This is still kind of useless. I'd say, let's not worry about layer breaks, or burning discs, or converting this to MS Windows. Personally, I'd rather make in work in Linux, make it work for all protections, and then go from there. Despite the cries and pleas from others! I have actually been spending my time reading specs and making sure that what is reported is correct.
Here are some specs/info that you may find useful:
http://dvd.wwwdotorg.org/specs/specs.html
http://www.tinyted.net/eddie/css_auth.html
http://read.pudn.com/downloads43/ebook/147287/ATAPI-DVD_SFF8090r99.pdf
spotter
23rd November 2010, 16:02
I'm mostly trying to figure out how to duplicate the dvd decrypter (which does generate the mds file).
some of these things aren't that important, but I'm also documenting them for myself so I can find them in the future.
setarip_old
4th December 2010, 00:06
Have I missed something about your project up until now? - Is playback limited to .ISO image files played on your PC - or can you also burn a disc that's playable on a standalone player?
xenex
4th December 2010, 11:52
@setarip_old
Isn't this just a repeat of the same question you asked, which was asked and answered on page four of this thread, here?:
http://forum.doom9.org/showthread.php?p=1454017#post1454017
This whole approach is fairly simple, really. No rocket science is needed. The output of spotter's program is an essentially an EXACT IMAGE of the original DVD with two exceptions.
1) Where the ORIGINAL DVD had bad/corrupt/unreadable sectors, ddrescue replaces them with blank/empty sectors in the ISO.
2) Where the ORIGINAL DVD had CSS-ENCRYPED sectors (VOB files) spotter's program overwrites them with CSS-DECRYPTED sectors.
That's it, basically. Two steps. Nothing else going on.
So, if the output ISO is small enough to be burned to a DVD5 SL writable medium, no problem, it will work.
If the output ISO is small enough to be burned to a DVD9 DL writable medium, it can be done, but it will absolutely require the layer break to be recalculated, probably using ImgBurn, which does a good job of it.
If the output ISO is HUGE and too large to be burned to a DVD9 DL writable medium, it can't be done at all without some 'editing.'
Personally, I NEVER burn movies to shiny discs. Do people still do this, LOL. I just save the ISO images on a drive and play them from there.
To each their own, I suppose.
setarip_old
5th December 2010, 01:09
@xenex
It was not answered with certainty, only speculated upon by the O.P. - and, based on your remarks in your most recent post #134 that: So, if the output ISO is small enough to be burned to a DVD5 SL writable medium, no problem, it will work.
If the output ISO is small enough to be burned to a DVD9 DL writable medium, it can be done, but it will absolutely require the layer break to be recalculated, probably using ImgBurn, which does a good job of it.You too, are speculating. Since "spotter" has stated that he is reluctant to provide a Windows version for the masses (including me) who could readily test the theory, perhaps he, or another Linux user, could simply burn a couple of such DVD backups to disc (e.g. one single layer sample and one double layer sample) and report the results of actual testing...
BTW, your remark regarding your personal preferences:Personally, I NEVER burn movies to shiny discs. Do people still do this, LOL.Has nothing to do with the question at hand, namely, will such a saved backup play properly from a burned disc on a standalone player?
spotter
5th December 2010, 01:23
I don't have a standalone player. all I have is my linux laptop where I do most of my real work, and my toy windows desktop (don't even own a TV!). I guess if I was thinking in advance, I could have burned a DVD5 test (have a handful of DVDs that would fit) and tried it out on my parents standalone players when I was there for thanksgiving weekend, but didn't think to.
with that said, I agree with xenex, I would be shocked if my decrypted ISO didn't work on a standalone player. It's just a DVD without any CSS.
The one thing that would amuse me, I'm wondering if there are any DVD players that basically implement libdvdcss, i.e. instead of doing the "proper" way to decode the DVDs, they basically ignore the "unwritable" area of the DVD and just automatically crack each DVD, and then even if you would burn a DVD with css scrambled sectors, it would still play perfectly.
spotter
5th December 2010, 01:29
also, if you really wanted to test this.
http://www.vmware.com/products/player/
http://www.vmware.com/appliances/directory/va/752043
setarip_old
5th December 2010, 01:55
@spotter also, if you really wanted to test this.As I mentioned previously, I know nothing about Linux, nor do I understand how I would use "VMWare" to test your program that I've never seen, nor have the ability to build.
I would be shocked if my decrypted ISO didn't work on a standalone player. It's just a DVD without any CSS.You may discover that standalone DVD players interpret damaged sectors differently than they do zero-filled sectors...
spotter
5th December 2010, 03:04
ok, and I'm not really a windows progammer :)
qyot27
5th December 2010, 08:25
@spotter As I mentioned previously, I know nothing about Linux, nor do I understand how I would use "VMWare" to test your program that I've never seen, nor have the ability to build.
VMWare is a virtual machine - you use it to install a copy of Linux to a virtual hard drive file without touching the existing Windows setup. And then you bring up the virtual machine (VMWare) and use a different OS (Linux) without logging out of the one you're currently in (Windows). From what I understand, Windows 7 uses a virtual machine approach if you use XP mode.
Personally, I'd use VirtualBox instead of VMWare, but it's the same premise. Well, actually, I'd forgo using a virtual machine at all and just install Ubuntu inside Windows using their Wubi installer if I didn't want to properly set the machine up to dual-boot. If you do it through Wubi there's little performance penalty (unlike a virtual machine, which is highly dependent on your hardware setup if you want to get decent performance out of it), and it can be uninstalled from Windows just as easily as it was installed.
If you're interested, I can provide step-by-step instructions through the whole process, either with Linux or with Windows.
The install for Ubuntu with Wubi (and the rest of the setup process the first time you go into Ubuntu) is pretty self-explanatory, so I don't think I'd need to go too in-depth on that, but to show how to build the program using a basic install I can do pretty easily (and it just requires some copying and pasting, not anything big; no real Linux knowledge needed for it either).
I could also explain how to do it with MinGW and MSys so that you can build a native Windows version without ever installing Ubuntu, but that's a longer process because it's more involved to get everything set up correctly. On Ubuntu you can do it with a single copy-pasted command.
setarip_old
5th December 2010, 09:31
@qyot27
Hi!
Thank you for your kind and generous offer - but it would be far simpler if someone already versed in Linux would use the Linux tool created by "spotter" to burn a backup disc (or two) and see if it plays on a standalone DVD player...
xenex
5th December 2010, 10:33
@setarip_old
I'd actually be happy to test the "burn a disc" issue, but the problem on my end is that I don't have any DVDs whose resultant ISO would be small enough to fit on a SL disc, and I don't own any writeable DL discs. I can't just go and buy some either, easily, as the small town in which I live has only a Wal-Mart which only sells crappy Memorex media, and does not even offer DL discs!
I'll ask Santa for some and see if they appear in my stocking. :)
But really, I think the whole "burn a disc" thing is getting the cart before the horse, honestly. Spotter's 'approach' is solid, but there are some problems in the implementation. Using the "ddrescue" program on DVDs that have many bad/corrupt sectors can take a LONG LONG TIME.
Just earlier, I got the new "Twilight: Eclipse" DVD and it has a RIDICULOUS UNHOLY number of intentional bad sectors. It took ddrescue hours to read it.
And, spotter's program isn't performing the CSS decryption in the optimal way. It's not a bug, per se, but an implementation issue.
@spotter
Here's what I mean, from libdvdcss.c:
* \li \b DVDCSS_METHOD: sets the authentication and decryption method
* that \e libdvdcss will use to read scrambled discs. Can be one
* of \c title, \c key or \c disc.
* - \c key is the default method. \e libdvdcss will use a set of
* calculated player keys to try and get the disc key. This can fail
* if the drive does not recognize any of the player keys.
* - \c disc is a fallback method when \c key has failed. Instead of
* using player keys, \e libdvdcss will crack the disc key using
* a brute force algorithm. This process is CPU intensive and requires
* 64 MB of memory to store temporary data.
* - \c title is the fallback when all other methods have failed. It does
* not rely on a key exchange with the DVD drive, but rather uses a
* crypto attack to guess the title key. On rare cases this may fail
* because there is not enough encrypted data on the disc to perform
* a statistical attack, but in the other hand it is the only way to
* decrypt a DVD stored on a hard disc, or a DVD with the wrong region
* on an RPC2 drive.
Since spotter is trying to decrypt an ISO on the hard disc, the two (better) methods 'key' and 'disc' are unavailable. So his program uses the final resort 'title' method which fails badly on small/short VOB files.
I've tested the heck out of this and know what is happening. And I'm working on a solution, but I only have an hour or two per day to work on it. I really am working on it, though, even if it will start out as a "proof of concept."
So, I hope that's clear as to why "burn a disc" is on my back burner. I would rather get this whole program sorted out in Linux, and working well, and solid first. And I really am working on it. I only have a few hours a day to spend, but I really am trying. I want a good Linux DVD ripper, I've always wondered why no one else has done it before, but spotter's idea inspired me.
setarip_old
9th December 2010, 23:27
So, I hope that's clear as to why "burn a disc" is on my back burner.Actually, it would be more interesting to hear from someone who HAS been able to actually test the performance of "spotter's" program with a burned disc (with both a standalone player and a software player - and, if it should come to pass, if someone releases a Windows version, so that MANY users can simply burn a disc or two to test it...)
spotter
10th December 2010, 18:46
@setarip_old
I'd actually be happy to test the "burn a disc" issue, but the problem on my end is that I don't have any DVDs whose resultant ISO would be small enough to fit on a SL disc, and I don't own any writeable DL discs. I can't just go and buy some either, easily, as the small town in which I live has only a Wal-Mart which only sells crappy Memorex media, and does not even offer DL discs!
I'll ask Santa for some and see if they appear in my stocking. :)
But really, I think the whole "burn a disc" thing is getting the cart before the horse, honestly. Spotter's 'approach' is solid, but there are some problems in the implementation. Using the "ddrescue" program on DVDs that have many bad/corrupt sectors can take a LONG LONG TIME.
Just earlier, I got the new "Twilight: Eclipse" DVD and it has a RIDICULOUS UNHOLY number of intentional bad sectors. It took ddrescue hours to read it.
And, spotter's program isn't performing the CSS decryption in the optimal way. It's not a bug, per se, but an implementation issue.
@spotter
Here's what I mean, from libdvdcss.c:
* \li \b DVDCSS_METHOD: sets the authentication and decryption method
* that \e libdvdcss will use to read scrambled discs. Can be one
* of \c title, \c key or \c disc.
* - \c key is the default method. \e libdvdcss will use a set of
* calculated player keys to try and get the disc key. This can fail
* if the drive does not recognize any of the player keys.
* - \c disc is a fallback method when \c key has failed. Instead of
* using player keys, \e libdvdcss will crack the disc key using
* a brute force algorithm. This process is CPU intensive and requires
* 64 MB of memory to store temporary data.
* - \c title is the fallback when all other methods have failed. It does
* not rely on a key exchange with the DVD drive, but rather uses a
* crypto attack to guess the title key. On rare cases this may fail
* because there is not enough encrypted data on the disc to perform
* a statistical attack, but in the other hand it is the only way to
* decrypt a DVD stored on a hard disc, or a DVD with the wrong region
* on an RPC2 drive.
Since spotter is trying to decrypt an ISO on the hard disc, the two (better) methods 'key' and 'disc' are unavailable. So his program uses the final resort 'title' method which fails badly on small/short VOB files.
I've tested the heck out of this and know what is happening. And I'm working on a solution, but I only have an hour or two per day to work on it. I really am working on it, though, even if it will start out as a "proof of concept."
So, I hope that's clear as to why "burn a disc" is on my back burner. I would rather get this whole program sorted out in Linux, and working well, and solid first. And I really am working on it. I only have a few hours a day to spend, but I really am trying. I want a good Linux DVD ripper, I've always wondered why no one else has done it before, but spotter's idea inspired me.
ah, make sense, I had to resort to ddrescue due to the original goal was to recover scratched media, but a decrypter/ddrescue combo tool would have access to all the keys. But it makes sense while the final resort method wouldn't work because there's not enough data to crack the key.
mc2man
12th December 2010, 12:32
burn a backup disc (or two) and see if it plays on a standalone DVD player...
It works quite fine, no issues to speak of other than a small 'oddity' with one title as far as LB
There are certainly some limitations to the method though there shouldn't be any problem with any disk that can playback in linux. (have seen a very small number that won't.
Due to having to 'read' thru bad sectors rather than exclude i won't consider any title that has more than 5 - 7 thousand unreadable sectors, like most sony protections. A typical ARccOS disk may take several hours - hardly worth it.
Here with a drive that recovers nicely from read errors it's about 1.2 sec.'s/block of 16 sectors so with most of the current protections time isn't an issue.
The structure protection, just like on encrypted dumps to .iso's on a hdd, is irrelevant.
Used 2 titles, both with less than 1000 bad sectors and some form of sp. - The Last Airbender which also misrepresents size and Ratatouille because it needed backing up and has a fair # of extras, easter eggs, ect.
For dumping used dvdisaster instead, used cdemu to mount the .iso's both before and after using spotter's code, the css encryption was nicely removed.
As far as burning to dl, again no real issue. While the rat movie could have been extracted to file for imgburn to create an iso and mds, didn't bother. It gave 1 choice for Lb and the burned disk works fine in a standalone in all regards.
(did extract later to ck, in that case imgburn showed 4 possible
The airbender iso had to be burned as is, in this case imgburn showed 9 possible Lb's, all at the same LBA but several different VTS's, PGC's, chapters, ect.
One seemed clearly obvious, not sure if it would have mattered.
Again the disk plays perfectly in a standalone.
spotter
14th December 2010, 17:32
yes, I'm dealing right now with Kick Ass, it seems to have a huge (100s of MBs of invalid sectors). Pondering if anything can be done to make it better.
Another thing I'm wondering, for non whole disk decryption, why hasn't anyone combined DVD Player w/ Menu UI to initiate the decryption i.e. use libdvdnav to initiate the dvd vm and just read blocks one after the other as the dvdnav says to. Only thing I can see going wrong with this approach is that perhaps you don't read every "track", but I'd think that would occur afterwards in the demuxer, but could be wrong.
spotter
14th December 2010, 22:56
so did some timing measurements on my laptop for the invalid sectors in kick ass.
I read each 2048 byte block in turn and measured how long it took (mostly for measuring how long it takes invalid sectors to return). doesn't seem to make a difference with or without O_DIRECT. Obviously way too long for a disk with a huge numer of invalid sectors.
xenex
15th December 2010, 11:29
@spotter
So, I modified your program (a bit) and I like this output better - this is the same "Kick Ass" DVD you are working on. If you can verify that the sectors match, it will help me:
xenex@MBOXL:~/Desktop$ ./a.out /dev/sr0 -test
total_blocks = 4169920
video_ts.ifo : 0000416 -> 0000433 : (18 Blocks)
video_ts.vob : 0000464 -> 0000520 : (57 Blocks)
video_ts.bup : 0000544 -> 0000561 : (18 Blocks)
vts_01_0.ifo : 0158085 -> 0158205 : (121 Blocks)
vts_01_0.vob : 0158206 -> 0313954 : (155749 Blocks)
vts_01_1.vob : 0313955 -> 0838241 : (524287 Blocks)
vts_01_2.vob : 0838242 -> 1362528 : (524287 Blocks)
vts_01_3.vob : 1362529 -> 1886815 : (524287 Blocks)
vts_01_4.vob : 1886816 -> 2411102 : (524287 Blocks)
vts_01_5.vob : 2411103 -> 2930154 : (519052 Blocks)
vts_01_0.bup : 2930155 -> 2930275 : (121 Blocks)
vts_02_0.ifo : 2930276 -> 2930290 : (15 Blocks)
vts_02_0.vob : 2930291 -> 2930347 : (57 Blocks)
vts_02_1.vob : 2930348 -> 3338552 : (408205 Blocks)
vts_02_0.bup : 3338553 -> 3338567 : (15 Blocks)
vts_03_0.ifo : 3338568 -> 3338576 : (9 Blocks)
vts_03_0.vob : 3338577 -> 3338633 : (57 Blocks)
vts_03_1.vob : 3338634 -> 3387378 : (48745 Blocks)
vts_03_0.bup : 3387379 -> 3387387 : (9 Blocks)
vts_04_0.ifo : 3387388 -> 3387396 : (9 Blocks)
vts_04_0.vob : 3387397 -> 3387453 : (57 Blocks)
vts_04_1.vob : 3387454 -> 3412613 : (25160 Blocks)
vts_04_0.bup : 3412614 -> 3412622 : (9 Blocks)
vts_05_0.ifo : 3412623 -> 3412631 : (9 Blocks)
vts_05_0.vob : 3412632 -> 3412688 : (57 Blocks)
vts_05_1.vob : 3412689 -> 3463087 : (50399 Blocks)
vts_05_0.bup : 3463088 -> 3463096 : (9 Blocks)
vts_06_0.ifo : 3463097 -> 3463105 : (9 Blocks)
vts_06_0.vob : 3463106 -> 3463162 : (57 Blocks)
vts_06_1.vob : 3463163 -> 3513561 : (50399 Blocks)
vts_06_0.bup : 3513562 -> 3513570 : (9 Blocks)
vts_07_0.ifo : 3513571 -> 3513579 : (9 Blocks)
vts_07_0.vob : 3513580 -> 3513636 : (57 Blocks)
vts_07_1.vob : 3513637 -> 3556439 : (42803 Blocks)
vts_07_0.bup : 3556440 -> 3556448 : (9 Blocks)
vts_08_0.ifo : 3556449 -> 3556457 : (9 Blocks)
vts_08_0.vob : 3556458 -> 3556514 : (57 Blocks)
vts_08_1.vob : 3556515 -> 3577791 : (21277 Blocks)
vts_08_0.bup : 3577792 -> 3577800 : (9 Blocks)
vts_09_0.ifo : 3577801 -> 3577809 : (9 Blocks)
vts_09_0.vob : 3577810 -> 3577866 : (57 Blocks)
vts_09_1.vob : 3577867 -> 3598148 : (20282 Blocks)
vts_09_0.bup : 3598149 -> 3598157 : (9 Blocks)
vts_10_0.ifo : 3598158 -> 3598166 : (9 Blocks)
vts_10_0.vob : 3598167 -> 3598223 : (57 Blocks)
vts_10_1.vob : 3598224 -> 3620886 : (22663 Blocks)
vts_10_0.bup : 3620887 -> 3620895 : (9 Blocks)
vts_11_0.ifo : 3620896 -> 3620904 : (9 Blocks)
vts_11_0.vob : 3620905 -> 3620961 : (57 Blocks)
vts_11_1.vob : 3620962 -> 3622771 : (1810 Blocks)
vts_11_0.bup : 3622772 -> 3622780 : (9 Blocks)
vts_12_0.ifo : 3622781 -> 3622789 : (9 Blocks)
vts_12_0.vob : 3622790 -> 3622846 : (57 Blocks)
vts_12_1.vob : 3622847 -> 3630323 : (7477 Blocks)
vts_12_0.bup : 3630324 -> 3630332 : (9 Blocks)
vts_13_0.ifo : 3630333 -> 3630341 : (9 Blocks)
vts_13_0.vob : 3630342 -> 3630398 : (57 Blocks)
vts_13_1.vob : 3630399 -> 3631793 : (1395 Blocks)
vts_13_0.bup : 3631794 -> 3631802 : (9 Blocks)
vts_14_0.ifo : 3631803 -> 3631811 : (9 Blocks)
vts_14_0.vob : 3631812 -> 3631868 : (57 Blocks)
vts_14_1.vob : 3631869 -> 3639345 : (7477 Blocks)
vts_14_0.bup : 3639346 -> 3639354 : (9 Blocks)
vts_15_0.ifo : 3639355 -> 3639361 : (7 Blocks)
vts_15_1.vob : 3639363 -> 3639432 : (70 Blocks)
vts_15_0.bup : 3639433 -> 3639439 : (7 Blocks)
vts_16_0.ifo : 3639440 -> 3639452 : (13 Blocks)
vts_16_1.vob : 3639454 -> 3657793 : (18340 Blocks)
vts_16_0.bup : 3657794 -> 3657806 : (13 Blocks)
vts_17_0.ifo : 3657807 -> 3657824 : (18 Blocks)
vts_17_1.vob : 3657826 -> 3694225 : (36400 Blocks)
vts_17_0.bup : 3694226 -> 3694243 : (18 Blocks)
vts_18_0.ifo : 3694244 -> 3694530 : (287 Blocks)
vts_18_1.vob : 3694532 -> 3891021 : (196490 Blocks)
vts_18_0.bup : 3891022 -> 3891308 : (287 Blocks)
vts_19_0.ifo : 3891309 -> 3891344 : (36 Blocks)
vts_19_1.vob : 3891346 -> 3989415 : (98070 Blocks)
vts_19_0.bup : 3989416 -> 3989451 : (36 Blocks)
vts_20_0.ifo : 3989452 -> 3989494 : (43 Blocks)
vts_20_0.vob : 3989495 -> 4027588 : (38094 Blocks)
vts_20_1.vob : 4027589 -> 4138748 : (111160 Blocks)
vts_20_0.bup : 4138749 -> 4138791 : (43 Blocks)
spotter
15th December 2010, 16:09
basically the same (without modifying my program to output every file, a cursory look at the VOBs seem to indicate same size
total_blocks = 4169920
video_ts.vob: 464->520 (57 blocks)
vts_01_0.vob: 158206->313954 (155749 blocks)
vts_01_1.vob: 313955->838241 (524287 blocks)
vts_01_2.vob: 838242->1362528 (524287 blocks)
vts_01_3.vob: 1362529->1886815 (524287 blocks)
vts_01_4.vob: 1886816->2411102 (524287 blocks)
vts_01_5.vob: 2411103->2930154 (519052 blocks)
vts_02_0.vob: 2930291->2930347 (57 blocks)
vts_02_1.vob: 2930348->3338552 (408205 blocks)
vts_03_0.vob: 3338577->3338633 (57 blocks)
vts_03_1.vob: 3338634->3387378 (48745 blocks)
vts_04_0.vob: 3387397->3387453 (57 blocks)
vts_04_1.vob: 3387454->3412613 (25160 blocks)
vts_05_0.vob: 3412632->3412688 (57 blocks)
vts_05_1.vob: 3412689->3463087 (50399 blocks)
vts_06_0.vob: 3463106->3463162 (57 blocks)
vts_06_1.vob: 3463163->3513561 (50399 blocks)
vts_07_0.vob: 3513580->3513636 (57 blocks)
vts_07_1.vob: 3513637->3556439 (42803 blocks)
vts_08_0.vob: 3556458->3556514 (57 blocks)
vts_08_1.vob: 3556515->3577791 (21277 blocks)
vts_09_0.vob: 3577810->3577866 (57 blocks)
vts_09_1.vob: 3577867->3598148 (20282 blocks)
vts_10_0.vob: 3598167->3598223 (57 blocks)
vts_10_1.vob: 3598224->3620886 (22663 blocks)
vts_11_0.vob: 3620905->3620961 (57 blocks)
vts_11_1.vob: 3620962->3622771 (1810 blocks)
vts_12_0.vob: 3622790->3622846 (57 blocks)
vts_12_1.vob: 3622847->3630323 (7477 blocks)
vts_13_0.vob: 3630342->3630398 (57 blocks)
vts_13_1.vob: 3630399->3631793 (1395 blocks)
vts_14_0.vob: 3631812->3631868 (57 blocks)
vts_14_1.vob: 3631869->3639345 (7477 blocks)
vts_15_1.vob: 3639363->3639432 (70 blocks)
vts_16_1.vob: 3639454->3657793 (18340 blocks)
vts_17_1.vob: 3657826->3694225 (36400 blocks)
vts_18_1.vob: 3694532->3891021 (196490 blocks)
vts_19_1.vob: 3891346->3989415 (98070 blocks)
vts_20_0.vob: 3989495->4027588 (38094 blocks)
vts_20_1.vob: 4027589->4138748 (111160 blocks)
though i'm not finishing scanning for bad blocks, and once the initial scan is done, will go over it once more. (currently have about 243MB of "bad" blocks with 71 areas that ddrescue still has to thoroughly go through)
spotter
3rd January 2011, 01:51
so xenex, I've now come across a bunch of DVDs that have the non decrypting issue you encountered, and you're right that it probably work if one had the disc's key, but I'm still wondering if there's no way to decrypt those small ones without the disc's key. or perhaps to read the disc key during copying and then to somehow set it in my program.
xenex
3rd January 2011, 08:24
@spotter
Right, that's one of the things I have been working on. The issue is that the 'disc key' is stored in the lead-in area of the disc, which cannot and does not get copied to an ISO. With trying to decrypt an ISO file, the "brute force" method is the only method available.
The "I/O Key Exchange" method (getting disc key) can only be done with the actual physical disc in an actual physical drive using ioctls. I already have some (very unfinished) code that I've been hacking at that will already authenticate the drive and get the disc key.
It can also get the title key for any VOB given a sector LBA, but I have not yet combined it with your program. It's very much work in progress on my end, and I only work on it as I can, and I am very slow.
I have uploaded that code to a free file sharing site, check your PM. Maybe you will see where I am going (or trying to go) with this. As you mentioned, I am only doing this for my own fun and education at this time. I have no guarantees that what I am trying will end up useful.
XAvAX
7th February 2011, 10:06
This is really nice, it's working really well for me. I think it may be possible to do a similar 'so simple it works' method for filesystem copies as a separate tool that is run on the ISO this generates. Based on the idea of 'garbage collection' in CS, it would take the IFOs a standard DVD player would play, read them to find out what they reference, repeat for any new IFOs found this way, note all the VOBs found this way, and delete everything left unreferenced when you run out of unresolved references. Maybe it could be called 'dvdgc'.
spotter
8th February 2011, 00:13
yes, that would work, the tricky part is (like in many GC systems), figuring out what are the appropriate starting points so you don't miss out on relatively self contained but valid loops.
sl1pkn07
8th February 2011, 00:25
hello. this is the last code?
http://forum.doom9.org/showpost.php?p=1454328&postcount=71
thanks
XAvAX
8th February 2011, 00:39
yes, that would work, the tricky part is (like in many GC systems), figuring out what are the appropriate starting points so you don't miss out on relatively self contained but valid loops.
Looking at the DVD spec, the only IFO that a real DVD player will read initially is VIDEO_TS.IFO - anything not linked (directly or indirectly) from that is unreachable. So that makes finding the starting point easy - It's a single root. The only challenge I can see is making sure you don't loop forever for circular references, but that's probably solvable by having a hash or map from filename to a boolean 'have we seen it' value, and abort processing of a subtree on a true result of a lookup. It might be necessary to actually parse the menu data though, to prevent IFOs that reference evil VOBs but don't provide a menu entry to actually play them to the user.
spotter
11th January 2012, 04:39
just as a followup, haven't modified the code in about a year. In practice it hasn't been as useful for me as I'd want as I want directory structures that are playable in windows media center. However, I ran into a problem with the copy of dvdfab that I've been using on Thor. Instead of upgrading right away, I tried out my methodology on it. Worked perfectly, to create a decrypted ISO image (that of course looks like a very large file system, but plays perfectly when mounted)
vBulletin® v3.8.11, Copyright ©2000-2026, vBulletin Solutions Inc.