Expand CSS Stevenson crack patterns, scan 50K sectors
- Add padding stream (0xBE) with 0xFF payload patterns - Add video/audio PES with multiple flag/header combinations - Add navigation pack system header pattern - Scan up to 50K consecutive scrambled sectors (was 500 sampled)
This commit is contained in:
+27
-20
@@ -197,28 +197,36 @@ pub fn crack_title_key(sector: &[u8]) -> Option<[u8; 5]> {
|
|||||||
//
|
//
|
||||||
// We try multiple stream IDs and use zeros for unknown bytes (most common).
|
// We try multiple stream IDs and use zeros for unknown bytes (most common).
|
||||||
|
|
||||||
let stream_ids: &[u8] = &[
|
// Try many PES header patterns at byte 0x80.
|
||||||
0xE0, // video
|
// Structure: 00 00 01 [stream_id] [len_hi] [len_lo] [flags1] [flags2] [hdr_len] [data]
|
||||||
0xBD, // private stream 1 (AC3/DTS)
|
let mut patterns: Vec<[u8; 10]> = Vec::with_capacity(128);
|
||||||
0xC0, // MPEG audio
|
|
||||||
0xBE, // padding
|
|
||||||
];
|
|
||||||
|
|
||||||
for &sid in stream_ids {
|
// Padding stream (0xBE): payload is 0xFF bytes, various lengths
|
||||||
// Build candidate plaintext (10 bytes)
|
for len_hi in 0u8..8 {
|
||||||
// Bytes 0-2: PES start code 00 00 01
|
for len_lo_top in [0x00u8, 0x80, 0xFF] {
|
||||||
// Byte 3: stream ID
|
patterns.push([0x00, 0x00, 0x01, 0xBE, len_hi, len_lo_top, 0xFF, 0xFF, 0xFF, 0xFF]);
|
||||||
// Bytes 4-9: we try with zeros first (common for padding streams)
|
}
|
||||||
// and with typical PES header bytes
|
}
|
||||||
let patterns: &[[u8; 10]] = &[
|
|
||||||
[0x00, 0x00, 0x01, sid, 0x00, 0x00, 0x80, 0x80, 0x05, 0x21],
|
|
||||||
[0x00, 0x00, 0x01, sid, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00],
|
|
||||||
[0x00, 0x00, 0x01, sid, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
|
|
||||||
];
|
|
||||||
|
|
||||||
for pattern in patterns {
|
// Video (0xE0) and audio (0xBD, 0xC0) with typical PES headers
|
||||||
|
for &sid in &[0xE0u8, 0xBD, 0xC0] {
|
||||||
|
for &flags1 in &[0x80u8, 0x81, 0x84, 0x85, 0x8C, 0x8D] {
|
||||||
|
for &flags2 in &[0x00u8, 0x05, 0x80, 0xC0] {
|
||||||
|
let hdr_len = if flags2 & 0x80 != 0 { 0x05u8 } else { 0x00 };
|
||||||
|
let pts0 = if flags2 & 0x80 != 0 { 0x21u8 } else { 0x00 };
|
||||||
|
// Try with several PES lengths
|
||||||
|
for &len_hi in &[0x00u8, 0x07] {
|
||||||
|
patterns.push([0x00, 0x00, 0x01, sid, len_hi, 0x00, flags1, flags2, hdr_len, pts0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Navigation pack system header (0xBB)
|
||||||
|
patterns.push([0x00, 0x00, 0x01, 0xBB, 0x00, 0x12, 0x80, 0xC4, 0xE1, 0x04]);
|
||||||
|
|
||||||
|
for pattern in &patterns {
|
||||||
if let Some(key) = recover_title_key(sector, pattern) {
|
if let Some(key) = recover_title_key(sector, pattern) {
|
||||||
// Verify: the key should produce valid MPEG-2 when used to descramble
|
|
||||||
let mut test = sector.to_vec();
|
let mut test = sector.to_vec();
|
||||||
super::lfsr::descramble_sector(&key, &mut test);
|
super::lfsr::descramble_sector(&key, &mut test);
|
||||||
if test[0x80] == 0x00 && test[0x81] == 0x00 && test[0x82] == 0x01 {
|
if test[0x80] == 0x00 && test[0x81] == 0x00 && test[0x82] == 0x01 {
|
||||||
@@ -226,7 +234,6 @@ pub fn crack_title_key(sector: &[u8]) -> Option<[u8; 5]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -32,15 +32,13 @@ pub struct CssState {
|
|||||||
///
|
///
|
||||||
/// The Stevenson attack needs a sector where a PES header starts at byte
|
/// The Stevenson attack needs a sector where a PES header starts at byte
|
||||||
/// 0x80 (start of the encrypted region). This only happens when a new PES
|
/// 0x80 (start of the encrypted region). This only happens when a new PES
|
||||||
/// packet begins at exactly sector offset 128, which is uncommon. We scan
|
/// packet begins at exactly sector offset 128. We scan up to 50000
|
||||||
/// up to 500 scrambled sectors across all extents to find a crackable one.
|
/// scrambled sectors sequentially across all extents.
|
||||||
pub fn crack_key(reader: &mut dyn SectorReader, extents: &[Extent]) -> Option<CssState> {
|
pub fn crack_key(reader: &mut dyn SectorReader, extents: &[Extent]) -> Option<CssState> {
|
||||||
let mut tried = 0u32;
|
let mut tried = 0u32;
|
||||||
let max_tries = 500;
|
let max_tries = 50_000;
|
||||||
|
|
||||||
for ext in extents {
|
for ext in extents {
|
||||||
// Sample sectors spread across the extent
|
|
||||||
let step = (ext.sector_count / 100).max(1);
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
while i < ext.sector_count && tried < max_tries {
|
while i < ext.sector_count && tried < max_tries {
|
||||||
let mut buf = vec![0u8; 2048];
|
let mut buf = vec![0u8; 2048];
|
||||||
@@ -50,7 +48,10 @@ pub fn crack_key(reader: &mut dyn SectorReader, extents: &[Extent]) -> Option<Cs
|
|||||||
}
|
}
|
||||||
tried += 1;
|
tried += 1;
|
||||||
}
|
}
|
||||||
i += step;
|
i += 1;
|
||||||
|
}
|
||||||
|
if tried >= max_tries {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user