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:
MattJackson
2026-04-16 15:15:17 +00:00
parent 597f512eca
commit f95894bba3
2 changed files with 38 additions and 30 deletions
+31 -24
View File
@@ -197,37 +197,44 @@ 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
if let Some(key) = recover_title_key(sector, pattern) { for &sid in &[0xE0u8, 0xBD, 0xC0] {
// Verify: the key should produce valid MPEG-2 when used to descramble for &flags1 in &[0x80u8, 0x81, 0x84, 0x85, 0x8C, 0x8D] {
let mut test = sector.to_vec(); for &flags2 in &[0x00u8, 0x05, 0x80, 0xC0] {
super::lfsr::descramble_sector(&key, &mut test); let hdr_len = if flags2 & 0x80 != 0 { 0x05u8 } else { 0x00 };
if test[0x80] == 0x00 && test[0x81] == 0x00 && test[0x82] == 0x01 { let pts0 = if flags2 & 0x80 != 0 { 0x21u8 } else { 0x00 };
return Some(key); // 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) {
let mut test = sector.to_vec();
super::lfsr::descramble_sector(&key, &mut test);
if test[0x80] == 0x00 && test[0x81] == 0x00 && test[0x82] == 0x01 {
return Some(key);
}
}
}
None None
} }
+7 -6
View File
@@ -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;
} }
} }