Разрыв в нумерации треков первой сессии
Эту психованную ненормальную защиту мы рассмотрим лишь теоритически —– на тот случай, если вам попадет диск, первая сессия которого разорвана каким-то моральным уродом и по причине конфликта с вашим оборудованием читается неправильное или, что более вероятно, не читается вообще. На этот случай в уголовном кодексе имеется статья об умышленном вредительстве, а с точки зрения прав потребителя —– продажа товара заведомо не соответствующего соответствующим спецификациям —– незаконна и потому нейтралиция защит подобного типа на юридическом языке зовется не "взломом", а ремонтом товара, осуществляемого непосредственно самим покупателям. В общем, как говорится —– на всякую хитрую гайку найдется свой хакер с характерным пивным животом.
Изучение "повдадок" защиты мы начнем с создания своеобразной "лабораторной крысы" или тестового стенда —– это уж как вам будет угодно. Извлечем из архива CCD-файл, сохранившийся после наших экспериментов по созданию фиктивного трека во второй сессии (вы ведь храните все свои CCD-файлы, правда?) и отредактируем его так, как показано в листинге 6.35.ниже: Номер второго трека заменен на девятый, соотвественно, номер третьего трека второй сессии заменен на десятый для обеспечения корректной стыковки двух сессий.
Листинг 6.35. Создание разрыва в нумерации треков первой сессии
[Entry 1] | [Entry 1] | [Entry 3] | [Entry 3] | [Entry 8] | [Entry 8] | ||||||
Session=1 | Session=1 | Session=1 | Session=1 | Session=2 | Session=2 | ||||||
Point=0xa1 | Point=0xa1 | Point=0x02 à | Point=0x09 | Point=0xa0 | Point=0xa0 | ||||||
ADR=0x01 | ADR=0x01 | ADR=0x01 | ADR=0x01 | ADR=0x01 | ADR=0x01 | ||||||
Control=0x04 | Control=0x04 | Control=0x04 | Control=0x04 | Control=0x4 | Control=0x4 | ||||||
TrackNo=0 | TrackNo=0 | TrackNo=0 | TrackNo=0 | TrackNo=0 | TrackNo=0 | ||||||
AMin=0 | Amin=0 | AMin=0 | AMin=0 | AMin=0 | AMin=0 | ||||||
ASec=0 | Asec=0 | ASec=0 | ASec=0 | ASec=0 | ASec=0 | ||||||
AFrame=0 | AFrame=0 | AFrame=0 | AFrame=0 | AFrame=0 | AFrame=0 | ||||||
ALBA=-150 | ALBA=-150 | ALBA=-150 | ALBA=-150 | ALBA=-150 | ALBA=-150 | ||||||
Zero=0 | Zero=0 | Zero=0 | Zero=0 | Zero=0 | Zero=0 | ||||||
PMin=2 à | Pmin=9 | PMin=3 | PMin=3 | PMin=3 à | PMin=10 | ||||||
PSec=0 | Psec=0 | PSec=1 | PSec=1 | PSec=0 | PSec=0 | ||||||
PFrame=0 | PFrame=0 | PFrame=33 | PFrame=33 | PFrame=0 | PFrame=0 | ||||||
PLBA=8850 | PLBA=-1 | PLBA=13458 | PLBA=13458 | PLBA=8850 | PLBA=-1 | ||||||
[Entry 9] | [Entry 9] | [Entry 11] | [Entry 11] | [TRACK 1] | [TRACK 1] | ||||||
Session=1 | Session=1 | Session=1 | Session=1 | MODE=1 | MODE=1 | ||||||
Point=0xa1 | Point=0xa1 | Point=0x03 à | Point=0x010 | INDEX 1=0 | INDEX 1=0 | ||||||
ADR=0x01 | ADR=0x01 | ADR=0x01 | ADR=0x01 | ||||||||
Control=0x04 | Control=0x04 | Control=0x04 | Control=0x04 | TRACK 2] | [TRACK 9] | ||||||
TrackNo=0 | TrackNo=0 | TrackNo=0 | TrackNo=0 | MODE=1 | MODE=1 | ||||||
AMin=0 | Amin=0 | AMin=0 | AMin=0 | INDEX 1=0 | INDEX 1=0 | ||||||
ASec=0 | Asec=0 | ASec=0 | ASec=0 | ||||||||
AFrame=0 | AFrame=0 | AFrame=0 | AFrame=0 | [TRACK 3] à | [TRACK 10] | ||||||
ALBA=-150 | ALBA=-150 | ALBA=-150 | ALBA=-150 | MODE=1 | MODE=1 | ||||||
Zero=0 | Zero=0 | Zero=0 | Zero=0 | INDEX 1=0 | INDEX 1=0 | ||||||
PMin=3 à | Pmin=10 | PMin=6 | PMin=6 | ||||||||
PSec=0 | Psec=0 | PSec=1 | PSec=1 | ||||||||
PFrame=0 | PFrame=0 | PFrame=33 | PFrame=33 | ||||||||
PLBA=8850 | PLBA=-1 | PLBA=26958 | PLBA=26958 |
Разрыв в нумерации треков второй сессии
Хорошо, "хачить ломать" нумерацию треков первой сессии– саксь и маст дай, но вот реакция приводов на разрыв второй сесии уже не такая "психованная" и, если предпринять все необходимые предосторожности, вероятность конфликта с оборудованием будет сведена к разумному минимуму, которым можно смело пренебречь (читай —– ущерб от несанционированного копирования "голых" дисков намного превышают убытки от возврата "нечитающихся" дисков, правда, среди пользователей встречаются и такие, что за каждый конфликтующийх диск грозяться подать на вас в суд или просто оторвать вам головуяйца, правда, никому из моих знакомых их еще не отрывали, так что риск оказается недееспособным не так уж и велик).
Опираясь на опыт, приобретенный в процессе предыдующих экспериментов, мы без труда разорвем вторую сессию, изменив номер третьего трека на девятый (естественно, вы можете выбрать и другой номер, но только помните, что четные номера приносят несчастье).
При записи искаженного образа на диск Clone CDCloneCD неправильно определяет номер последнего трека разорванной сессии, ошибочно принимая выводную область за самостоятельный трек (листинг 6.37). Действительно, CloneCD некорректно определил количество треков второй сессии, ошибочно посчитав, что их восемь (в то время как правильный ответ: два), так же трек с номером 170 представляющий собой трек Lead-Out, ошибочно интерпретируется как трек с данными (совмещенные треки). Однако, на качество "проотжига" диска это никак не влиянет.
Листинг 6.37. CloneCD некорректно определяет количество треков второй сессии
ИНФОРМАЦИЯ О СЕССИИ 1:
Размер сессии: 4726 Кбайт
Число треков: 1
Track 1: Данные Mode 1, размер: 4726 Кбайт
ИНФОРМАЦИЯ О СЕССИИ 2:
Размер сессии: 3939 Кбайт
Число треков: 8
Track 2: Данные Mode 1, размер: 1722 Кбайт
Track 9: Данные Mode 1, размер: 2216 Кбайт
Track 170: Data, размер: 4294932446
Кбайт
"Листинг 29 Clone CD некорректно определил количество треков второй сессии, ошибочно посчитав, что их восемь (в то время как правильный ответ: два), так же трек с номером 170 представляет собой Lead-Out трек, ошибочно интерпретированный как трек с данными (совмещенные треки)
Полевые" испытания защищенного диска показывают следующие результаты. Приводы NEC и TEAC "видят" лишь первую сессию диска, а вторая оказывается недоступной даже на секторном уровне, однако, команды SEEK, READ SUBCHANNAL и READ HEADER исполняются успешно. Если бы аналогичным образом вели себя все приводы, —– разработчику защиты ничего бы не стоило разместить в Q-канале подкода второй сессии ключевую метку или просто проверить Q-канал "разорванной" сессии на читабемльность. Такие копировщики как Alcohol 120% Алкоголь и Clone CDCloneCD просто не увидят разованной сессии, а если даже и увидят, то не смогут скопировать ее содержимое, возвращаемое как уже говорилось, не в отдельном канале, а вместе с основным потоком данных. При условии, что разорванная сессия не доступна на секторном уровне (а это действительно так), на несанкционированных копиях диска ее просто не окажется и команды SEEK, READ SUBCHANNAL и READ HEADER возратят ошибку, позволяя тем самым отличить копию от оригинала.
Увы, некоторые приводы (ASUS в частности) не дают к разорванной сессии никакого доступа вообще, "благодаря" чему оригинальный диск ошибочно опознается защитой как копия. Так что закладываться на вторую сессию ни в коем случае нельзя! Впрочем, эти меры предосторожности практически не ослабляет защиту, поскольку даже целая сессия такого диска все равно не копируется.
Просмотр геометрии диска с помощьюпод копировщикаом Ahead Nero показывает, что последний не только не может определить длины треков разорванной сессии (которые по его мнению не имеют никакой длины вообще), он катострофически неправильно отображает их номера. Истинные номера треков, записанные в TOC'е на экран вообще не выводятся, замещаясь на последовательные порядковые номера (см. рис. 6.200x0103). Таким образом, трек номер девять представляется как трек с номером три. В некотором смысле это может быть и верно, но вот для корректного копирования защищенного диска одних лишь порядковых номеров оказывается недостаточно.
Рис. 6.20. унок 15 0x103 Ahed Nero неправильно отображает номера треков
Копировщик Clone CDCloneCD видит лишь первую сессию защищенного диска и, судя по всему, даже и не подозревает о существовании второй (см. листинг 6.38, приведенный ниже). Как следствие —– разорванная сессия вообще не копируется и в TOC'е диска-копии отсутствует всякое упоминание о ней. Таким образом, для проверки лицензионной чистоты диска защитому механизму достаточно лишь считать TOC и сравнить его с эталотнным TOC'ом оригинала.
Листинг 6.38. CloneCD неправильно отображает номера треков
ИНФОРМАЦИЯ О CD В ДИСКОВОДЕ:
Число сессий: 1
Занято на диске: 30911 Кбайт
Секторов: 13458
Время: 02:59:33 (мин:сек:кадр)
ИНФОРМАЦИЯ О СЕССИИ 1:
Размер сессии: 30911 Кбайт
Число треков: 3
Track 1: Данные Mode 1, размер: 30911 Кбайт
Листинг 30 Clone CD неправильно отображает номера треков
Ладно, с разорванной сессией все более или меннее понятно. Ну не рассчитывали создатели Clone CDCloneCD на такие "извращения", ну не додумалисьперли до того, что нумерация треков может быть коварно измена. Ведь не кастрировать же их за это, верно? Но ведь и первая сессия защищенного диска так же оказалась скопированной неверно (листинг 6.39).! Отсюда мораль: несвоевременная санкции предъявленные к разработчикам придают "неприятный привкус" программному продукту. (Отсюда мораль: несвоевременная кастрация разработчиков придает неприятный привкус программному продукту).
Листинг 6.39. Содержимое TOC оригинального диска (слева) и диска, скопированного CloneCD (справа)
[Entry 2] |
[Entry 2] |
; адрес выводной области первой сессии оказался |
Session=1 |
Session=1 |
; определен неправильно! Clone CD установил его |
Point=0xa2 |
Point=0xa2 |
; на адрес начала трека номер два (первого трека |
ADR=0x01 |
ADR=0x01 |
; второй сессии), что повлекло за собой искажение |
Control=0x04 |
Control=0x04 |
; длины первого трека и породило множество |
TrackNo=0 |
TrackNo=0 |
; нечитающихся секторов, расположенных в Lead-Out/ |
AMin=0 |
AMin=0 |
; Lead-In областях. Таким образом, для определения |
ASec=0 |
ASec=0 |
; подлинности диска вовсе необязательно читать |
AFrame=0 |
AFrame=0 |
; содержимое TOC'a и достаточно всего лишь |
ALBA=-150 |
ALBA=-150 |
; определить полную емкость носителя, что можно |
Zero=0 |
Zero=0 |
; сделать и штатными средствами операционной системы |
PMin=3 à |
PMin=0 |
; без обращения к интерфейсам ASPI32/SPTI |
PSec=1 à |
PSec=29 |
; альтернативный путь – прочитать содержимое |
PFrame=33 |
PFrame=33 |
; TOC командой IOCTL_CDROM_READ_TOC, что так же не |
PLBA=13458 à |
PLBA=2058 |
; требует обращения к ASPI32/SPTI |
[Entry 10] |
[Entry 4] |
; трек номер два, принадлежащий второй сесии, ; CloneCD |
Session=2 |
Session=1 |
; запихнул в конец первой, даже не потрудившись |
Point=0x02 |
Point=0x02 |
; сопоставить стартовый адрес последнего со ; стартовым |
ADR=0x01 |
ADR=0x01 |
; адресом выводной области первой сессии. Очевидно, |
Control=0x04 |
Control=0x04 |
; что они "волшебным" образом совпадают, вероятно |
TrackNo=0 |
TrackNo=0 |
; потому-то выводная область и принимается за ; самостоятельный трек |
AMin=0 |
AMin=0 |
; попытка чтения содержимого второго трека ни к чему |
ASec=0 |
ASec=0 |
; хорошему не приводит, а третий ; (ну то есть девятый) |
AFrame=0 |
AFrame=0 |
; трек вообще оказался потерян, поэтому копия диска, |
ALBA=-150 |
ALBA=-150 |
; полученная с помщью CloneCD отличается от ; оригинала, |
Zero=3 |
Zero=3 |
; как небо от земли и защите не будет стоит большого |
PMin=3 |
PMin=3 |
; труда обнаружить факт несанкционированного ; копирования |
PSec=1 |
PSec=1 |
; даже без обращений ко второй сессии! Вот такой он, |
PFrame=33 |
PFrame=33 |
; CloneCD! А еще претендует на звание дома высокой |
PLBA=13458 |
PLBA=13458 |
; культуры и быта, тьфу, на звание защищенного ; копира! |
AMin=0 AMin=0 ; Lead- In областях. Таким образом, для определения
ASec=0 ASec=0 ; подлинности диска вовсе необязательно читать
AFrame=0 AFrame=0 ; содержимое TOC'a и достаточно всего лишь
ALBA=-150 ALBA=-150 ; определить полную емкость носителя, что можно
Zero=0 Zero=0 ; сделать и штатными средствами операционной системы
PMin=3 à PMin=0 ; без обращения к интерфейсам ASPI32/SPTI
PSec=1 à PSec=29 ; альтернативный путь – прочитать содержимое
PFrame=33 PFrame=33 ; TOC командой IOCTL_CDROM_READ_TOC, что так же не
PLBA=13458 à PLBA=2058 ; требует обращения к ASPI32/SPTI
[Entry 10] [Entry 4] ; трек номер два, принадлежащий второй сесии, Clone CD
Session=2 Session=1 ; запихнул в конец первой, даже не потрудившись
Point=0x02 Point=0x02 ; сопоставить стартовый адрес последнего со стартовым
ADR=0x01 ADR=0x01 ; адресом выводной области первой сессии. Очевидно, что
Control=0x04 Control=0x04 ; они "волшебным" образом совпадают, вероятно потому-то
TrackNo=0 TrackNo=0 ; выводная область и принимается за самостоятельный трек
AMin=0 AMin=0 ; попытка чтения содержимого второго трека ни к чему
ASec=0 ASec=0 ; хорошему не приводит, а третий (ну то есть девятый)
AFrame=0 AFrame=0 ; трек вообще оказался потерян, поэтому копия диска,
ALBA=-150 ALBA=-150 ; полученная с помщью Clone CD отличается от оригинала,
Zero=3 Zero=3 ; как небо от земли и защите не будет стоит большого
PMin=3 PMin=3 ; труда обнаружить факт несанкционированного копирования
PSec=1 PSec=1 ; даже без обращений ко второй сессии! Вот такой он,
PFrame=33 PFrame=33 ; Clone CD! А еще претендует на звание дома высокой
PLBA=13458 PLBA=13458 ; культуры и быта, тьф,у на звание защищенного копира!
Листинг 31 содержимое TOC'а оригинального диска (слева) и диска, скопированного Clone CD (справа)
Alcohol 120% Алкоголик с копированием такого диска справляется не в пример лучше, однако, полученная с его помощью копия имеет по меньшей мере одно существенное отличие от оригинала. Номер разорванного трека непроизвольным образом меняется с девяти на три, то есть Alcohol 120% Алкоголик автоматически восстанавливает поврежденную нумерацию треков на правильную. Но ведь искаженная нумерация треков нам как раз и нужна! Защитный механизм, привязывающийся к TOC'у просто забракует такой диск, обозвав его несанкционированной копий! (Забвно, но содержимое указателяpoint'a A1h, указывающего на номер последнего трека диска остается неизменным и по прежнему равно девяти, то есть восстанавливая TOC, Alcohol 120% Алкоголик все равно восстановил его не корректно).
Таким образом, для привязки к оригинальному TOC'у, защитному механизму незачем считывать его содержимое в "сыром" виде и можно вполне обойтись штатными средствами операционной системы, поскольку искажения, вносимые копировщиками в TOC настолько велики, что в прямом смысле слова видны невооруженным глазом.
Тем не менее, стойкость защит данного типа достаточно невелика. Хакеру ничего не стоит вручную отредактировать образ диска, снятый Alcohol 120%Алкоголем, вернув разорванному треку его законный номер равный девяти или иному другому числу (конкретное значение легко узнать с помощью утилиты CD_READ_TOC или аналогичной ей).
Шифровка файлов
Для предотвращения пофайлового копирования в идеале следовало бы использовать собственныех нестандартныех форматы данных, просмотр/прослушивание которых в обход программы-оболочки был бы невозможным. Однако, разработка "своего" формата требует значительных вложений, которые все равно не будут оправданы, т.к. прежде чем программа окупит себя, хакеры успеют "отвязать" ее от диска, взломав секторную защиту и тем самым, получив возможность массового тиражирования носителя.
Поэтому, разработчики защиты предпочитают отталкиваться от уже существующих форматов (например, того же MP3), просто зашифровывая файлы перед записью на мастер-диск, а при проигрывании —– расшифровывая их содержимое "на лету". Минус такого подхода в том, что защиты подобного типа очень легко взломать, —– достаточно просто установить точку останова на функцию CreateFile и, дождавшись открытия нужного нам файла, "подсмотреть" значение регистра EAX на выходе из функции —– оно-то и будет представлять собой дескриптор открытого файла. Теперь остается лишь поставить точки останова на функции SetFilePointer/ReadFile, приказав отладчику "всплывать" только в случае передачи им "нашего" дескриптора. Точка останова, установленная на область памяти, содержащую прочитанные с диска данные, выведет хакера непосредственно на процедуру расшифровки, проанализировав алгоритм которой, хакер сможет написать свой собственный расшифровщик!
А, если алгоритм шифровки представляет собой тривиальную операциюый XOR (как в подавляющем большинстве случаев и наблюдается), взломать содержимое диска можно еще быстрее! Ведь практически все стандартные форматы файлов содержат в себе некоторое количество более или менее предсказуемой информации и потому могут быть расшифрованы атакой по открытому тексту. И AVI, и MP2/MP3, и WMA, и ASF- файлы содержат в себе длинные цепочки подряд идущих нулей (и/или символов с кодом "FF"), а потому ключ шифрования обнаруживается тривиальным просмотром содержимого защищенного файла в любом HEX-редакторе.
Рассмотрим следующий пример. Пусть у нас имеется мультимедийный диск "Depeche Mode The best", содержимое одного из файлов которого выглядит так как это показано в листинге 8.8.:
Листинг 8.8. Hex-дамп заголовка исследуемого файла
K:\sex\1\03 - Strangerlove.dat DOS 3472405
00000000: 9D 9A F0 39 62 61 60 3A ¦ 65 A4 8B F0 52 C1 01 98 ЭЪЁ9ba`:eдЛЁR+OШ
00000010: BA DB 03 5A 54 27 A6 4C ¦ 43 ED 46 1D 8B 21 ED 9A ¦-¦ZT'жLCэF-Л!эЪ
00000020: D3 C7 7B 58 4B A6 78 5D ¦ F6 FA F0 A9 55 63 66 A8 L¦{XKжx]Ў·ЁйUcfи
00000030: 7E 6A 5A 79 61 68 E8 7B ¦ 69 47 F9 7B 60 22 E3 88 ~jZyahш{iG•{`"уИ
00000040: 61 E2 67 98 E0 E2 2D ED ¦ 13 AD E3 38 C5 A5 71 FB aтgШрт-э!ну8+еqv
00000050: 1A 01 C0 B6 85 77 5A 49 ¦ 46 4F 93 7B BF 30 A5 9D >OL¦ЕwZIFOУ{¬0еЭ
Листинг 76 hex-дамп заголовка исследуемого файла
По внешнему виду это не похоже ни на MP3 (MP3-файлы начинаются с сигнатуры FF FB, прочем, не всегда расположенной в самом начале), ни на WAV (WAV-файлы начинаются с сигнатуры "RIFF"), ни на RealAudio (RealAudio-файлы начинаются с ".RMF"), но… ведь каким-то же образом они все-таки воспроизводятся! И вряд ли это собственный формат разработчиков мультимедийного диска. Скорее всего, файл просто зашифрован. А, раз так, то – его можно попробовать расшифровать!
Прокручиваем экран HEX-редактора вниз, пока не встречаем следующую регулярную последовательность (листинг 8.9).:
Листинг 8.9. Регулярная последовательность, обнаруженная внутри исследуемого файла
K:\sex\1\03 - Strangerlove.dat DOS 3472405
000001E0: C3 5A AF F8 70 4A D8 83 ¦ 5D 9E 9D 86 9D 9E 9D 86 +Zп°pJ+Г]ЮЭЖЭЮЭЖ
000001F0: 9D 9E 9D 86 9D 9E 9D 86 ¦ 9D 9E 9D 86 9D 9E 9D 86 ЭЮЭЖЭЮЭЖЭЮЭЖЭЮЭЖ
00000200: 9D 9E 9D 86 9D 9E 9D 86 ¦ 9D 9E 9D 86 9D 9E 9D 86 ЭЮЭЖЭЮЭЖЭЮЭЖЭЮЭЖ
00000210: 9D 9E 9D 86 AA C2 62 79 ¦ 62 B4 C0 6A 9D 9E 9D 86 ЭЮЭЖкTbyb+LjЭЮЭЖ
00000220: 9D 9E 9D 86 9D 9E 9D 86 ¦ 9D 9E 9D 86 9D 9E 9D 86 ЭЮЭЖЭЮЭЖЭЮЭЖЭЮЭЖ
00000230: 9D 9E 9D 86 9D 9E 9D 86 ¦ 9D 9E 9D 86 9D 9E 9D 86 ЭЮЭЖЭЮЭЖЭЮЭЖЭЮЭЖ
00000240: 9D 9E 9D 86 9D 9E 9D 86 ¦ 70 B8 46 0B 3B 61 63 30 ЭЮЭЖЭЮЭЖp¬F>;ac0
Листинг 77 регулярная последовательность обнаруженная внутри исследуемого файла
Весьма вероятно, что в оригинальном файле здесь располагалась цепочка байт с идентичными значениями, например, последовательность нулей или "FF", после выполнения операции поXORенная с неким четырехбайтовым ключом.
Поскольку, XOR —– симметричная операция, то ((A XOR B) XOR A) == B, то есть повторная шифровка файла его исходным содержимым дает нам ключ. Предположим, что на этом месте были нули, то – тогда искомый ключ шифрования будет равен ""…9E 9D 86 9D…"". Точки по обе стороны от ключа означают, что мы еще не готовы выделить начало и конец регулярной последовательности. В самом деле, это может быть как "9E 9D 86 9D", так и "9D 86 9E 9D", и даже "86 9D 9E 9D" или вообще "9D 9E 9D 86". Однако, вместо того, чтобы тупо перебирать все четыре варианта, давайте обратим внимание вот на что. Длина регулярной последовательности равна четырем, так? Следовательно, первый байт каждого "периода" должен лежать по смещению, кратноыму четырем. Отсюда, искомая последовательность имеет вид "9D 9E 9D 86", а все остальные варианты неверны, т. к. лежат по неподходящим адресам. Поскольку, начальные адреса HEX-строк, выводимых редактором, выровнены по границе 0х10 байт (а 0x10 кратно 4), то первый байт ключа должен совпадать с начальным адресом любой из строк.
Теперь допустим, что в данном месте оригинального файла находилась цепочка нулей. Тогда ключ шифровки должен выглядеть так: "9D 9E 9D 86"
(т. к. (A XOR 0) == A).
Запускаем HEX-редактор HIEW, нажатием <Enter> переводим его в шестнадцатеричный режим, давим <F3> для активизации режима редактирования, далее нажимаем <F8> и в появившееся диалоговое окно "Enter XOR mask" вводим следующую HEX-последовательность и, уронив "кирпич" на <F8>, идем пить чай, так как HIEW расшифровывает файл ну очень долго и потому ничего не остается, как садиться за свой любимый компилятор и писать собственную программу расшифровки. Листингу, Приведенному листингуниже (листинг 8.10), до образца программистского искусства, конечно, далеко, но как рабочий вариант, сделанный на скорую руку, он вполне сойдет.
Листинг 8.10. [/etc/DeXOR.c] Демонстрационный вариант простейшего шифровщика
/*----------------------------------------------------------------------------
*
* XORит СОДЕРЖИМОЕ ФАЙЛА ПО ПРОИЗВОЛЬНОЙ МАСКЕ
* ============================================
*
* Build 0x001 @ 09.07.2003
----------------------------------------------------------------------------*/
#include <stdio.h>
#define MAX_LEN 666 // макс длина маски
#define MAX_BUF_SIZE (100*1024) // размер буфера чтения
#define FDECODE "decrypt.dat" // имя расшифрованного файла
main(int argc, char **argv)
{
long a, b;
long key_len;
FILE *fin, *fout;
long buf_size, real_size;
unsigned char key[MAX_LEN];
unsigned char buf[MAX_BUF_SIZE];
if (argc<3) // HELP по ключам командой строки
{
fprintf(stderr,"USAGE: DeXOR.exe file_name AA BB CC DD EE...\n");
return 0;
}
// определение длины ключа и установка размера буфера кратной длине ключа
key_len = argc - 2; buf_size = MAX_BUF_SIZE - (MAX_BUF_SIZE % key_len);
// извлекаем ключи из командной строки в массив key
for(a = 0; a < key_len; a++)
{
// преобразование из HEX-ASCII в long
b = strtol(argv[a+2],&" ",16);
if (b > 0xFF) // проверка на предельно допустимое значение
{fprintf(stderr, "-ERR: val %x not a byte\x7\n",b); return -1;}
key[a] = b; // заносим значение очередного байта ключа
}
printf("build a key:"); // выводим ключ на экран (для контроля)
for(a=0;a<key_len;a++) printf("%02X",key[a]); printf("\n");
// открываем файлы на для чтения/записи
fin = fopen(argv[1],"rb"); fout=fopen(FDECODE,"wb");
if ((fin==0) || (fout==0))
{ fprintf(stderr, "-ERR: file open error\x7\n"); return -1; }
// основной цикл обработки
while(real_size=fread(buf,1, buf_size, fin))
{
// цикл по буферу
for (a=0; a<real_size; a+=key_len)
{
// цикл по ключу
for(b=0; b < key_len; b++)
buf[a+b] ^= key[b];
}
// скидываем зашифрованный (расшифрованный) буфер на диск
if (0 == fwrite(buf, 1, real_size, fout))
{
fprintf(stderr,"-ERR: file write error\x7\n");
return -1;
}
}
// сваливаем
}
Листинг 78 [/etc/DeXOR.c] демонстрационный вариант простейшего шифровщика
Компилируем и запускаем: ""DeXOR.c "03 – Strangerlove.dat" 9D 9E 9D 86" и… ничего не получается! Расшифрованный файл все равно не похож ни на MP3, ни на другие форматы. Что ж, значит это была не цепочка нулей, а что-то другое, например, последовательность символов "FF". В плане проверки нашей гипотезы выполним операцию "проXOR'им имнад" регулярнойуюую последовательностью 9D9E9D86h и числом FFFFFFh и, если нам повезет, в результате этой операции мы получим оригинальный ключ. Для осуществления задуманного нам вновь понадобиться HEX-редактор HIEW или штатный калькулятор Windows-калькулятор. Запустим его и, кликнув мышкой по меню "Вид"
найдем пункт "Инженерный". Теперь, выберем переместим радио-конпкуположение Hex (шестнадцатеричная система) для переключателя, ответственногоую за выбор системы исчисления из позиции Dec (десятичная система), в которой она находилась по умолчанию, в позицию "Hex" (шестнадцатеричная система), или же попросту нажмем <F5>. Используя мышь и/или клавиатуру введем "9D9E9D86", затем нажмем кнопочкукнопку с надписью "Xor" и введем "FFFFFFFF". Подтвердим серьезность своих намерений нажатием <Enter>. Калькулятор выдаст следующий результатответит: "62616279". Это и есть искомый ключ, вводим его в программу DeXOR, разделяя байты пробелами и…
…и после переименования Strangerlove.dat в Strangerlove.mp3 он соглашается воспроизводиться любым MP3-плейером. Аналогичным путем расшифровываются и все остальные файлы, находящиеся на защищенном диске (ключ шифрования у них разный, но методика его поиска общая).
Какие из этого следуют выводы? Если уж взялись шифровать файлы с предсказуемым содержимым, то выбирайте длину ключа шифрования так, чтобы она была сравнима с длиной этих предсказуемых последовательностей, а желательно —– в несколько раз превышала ее. Или же меняйте использование операции XOR при кодировании на более продвинутые алгоритмы шифрования (если, конечно, вам не лень их реализовывать).
На самом деле, задача получения длинного непериодического ключа элегантно решается с помощью… генератора случайных чисел (или точнее псевдослучайных
чисел). Как наверное помните, псевдослучайная последовательность, генерируемая библиотечной функцией rand() постоянная при каждом запуске программы, поэтому представляет собой отличный и в то же время категорически неочевидный ключ! Собственно говоря, приведенная далее ниже программа (листинг 8.11) именно так и делает.:
Листинг 8.11. [crackme.765B98ECh.c] Использование rand() для хранения ключа шифровки
/*----------------------------------------------------------------------------
*
* НЕЯВНАЯ ГЕНЕРАЦИЯ КЛЮЧА РАСШИФРОВКИ С ПОМОЩЬЮ RAND()
* ====================================================
*
* шифрует/расшифровывает файлы, используя функцию rand() для генерации
* ключа; поскольку функция rand() при каждом запуске дает одну и ту же
* последовательность, устанавливаемую srand(), мы получаем оччччень длинный
* и не периодичный ключ шифрования, "ослепляющий" атаку по открытому тексту.
* К тому же, если чуть-чуть изменить код rand(), то IDA не сможет ее
* распознать (правда, это не сильно усложнит взлом, т.к. реализация функции
* rand() в подавляющем большинстве случаев до смешного проста).
* дополнительные уровни защиты обеспечиваются запутываем алгоритма
* обработки данных (можно, например, создать целую серию расшифровщиков,
* из которых полезные данные будет выдавать только один, а остальные
* станут взращать бессмысленный мусор)
*
* NOTE: чтобы зашифровать оригинальный файл, запустите программу
* с ключом "-crypt" это достаточно сделать один раз (файл, поставляемый
* на CD-диске, прилагаемом к книге, уже зашифрован и попытка его повторной
* зашифровке приведет к прямо противоположному результату - файл будет
* записан на диск в расшифрованном виде)
*
* Build 0x001 09.07.2003
----------------------------------------------------------------------------*/
#include <stdio.h>
#include <math.h>
#define FNAME "file.dat" // имя файла для (за|ра)сшифровки
#define MAX_SIZE (100*1024) // максимально возможный размер файла
#define SEED 0x666 // установка последовательности rand()
// это число может быть любым, главное
// чтобы оно было!
//--[crypt]-------------------------------------------------------------------
// fname - имя файла
// buf - указатель на буфер, куда помещать расшифрованные данные
// buf_size - размер буфера
// need_store - требуется ли записывать (за|ра)сшифрованный файл на диск
// :0 - не записывать, != 0 - записывать
//----------------------------------------------------------------------------
crypt(char *fname, char *buf, int buf_size, int need_store)
{
FILE *f;
long a, b;
// не забудьте явно проинициализировать генератор случайных чисел, иначе
// если другие ветки программы так же используют rand(), результат
// расшифровки получается "плавающим"
srand(SEED);
// открытие расшифровываемого файла
f=fopen(fname, "rb"); if (f==0) return -1;
// загрузить данные в буфер
a = fread(buf, 1, buf_size, f); if (!a || (a == buf_size)) return -1;
// (за|ра) сшифровать содержимое буфера ключом, налету генерируемом
// функцией rand()
for (b = 0; b < a; b++) buf[b] ^= (rand() % 255); fclose(f);
// отладочный шлюз для автоматической шифровки файла
if (need_store)
{
f=fopen(fname, "wb"); if (f==0) return -1;
fwrite(buf, 1, a, f); fclose(f); return -1;
}
return a;
}
main(int argc, char** argv)
{
long a,x;
long need_store = 0;
unsigned char buf[MAX_SIZE];
// TITLE
fprintf(srderr,"crackme 765b98ec by Kris Kaspersky\n");
// если есть отладочный ключ -crypt зашифровать
if ((argc > 1) && !strcmp(argv[1],"-crypt")) need_store++;
// загрузить файл FNAME, расшифровать и вывести его содержимое на экран
if ((x=Crypt(FNAME, buf, MAX_SIZE, need_store))!=-1)
for (a=0;a<x;a++) printf("%c",buf[a]);
}
Синдромный декодер
Грубо говоря, синдром есть остаток деления декодируемого кодового слова c(x) на порожденный полином g(x), и, если этот остаток равен нулю, кодовое слово считается неискаженным. Ненулевой остаток свидетельствует о наличии по меньшей мере одной ошибки. Остаток от деления дает многочлен, независящий от исходного сообщения и определяемый исключительно характером ошибки (syndrome— греческое слово, обозначающее совокупность признаков и/или симптомов, характеризующих заболевание).
Примечание
(Syndrome (синдром) — греческое слово, обозначающее совокупность признаков и/или симптомов, характеризующих заболевание.)
Принятое кодовое слово v
с компонентами vi = ci + ei, где i = 0, … n – 1, представляет собой сумму кодового слова c
и вектора ошибок e. Цель декодирования состоит в очистке кодового слова от вектора ошибки, описываемым полиномом синдрома и вычисляемом по формуле Sj = v(aj + j0 – -1), где j изменяется от 1 до 2t, а a представляет собой примитивный член "альфа", который мы уже обсуждали в предыдущемй разделеглаве. Да, мы снова выражаем функцию деления через умножение, поскольку деление — крайне неэффективная в смысле производительности операция.
Блок схема устройства, осуществляющего вычисление синдрома приведена на рис. 21.44. Как видно, она основана представляет собой типичный фильтр (сравните ее со схемой рис. 21.2), а потому ни в каких дополнительных пояснениях не нуждается.
Рис. 21.4.4. 0x338 Блок-схема цепи вычисления синдрома
Вычисление синдрома ошибки происходит итеративно, так что вычисление результирующего полинома (также называемого ответом от английского "answer") завершается непосредственно в момент прохождения последнего символа четности через фильтр. Всего требуется 2t
циклов "прогона" декодируемых данных через фильтр, — по одному прогону на каждый символ результирующего полинома.
Пример простей программной реализации синдромного декодера содержится в листинге 21.20[Y71] , и он намного нагляднее его словесного описания.
Синхрогруппы, объединяющие битыmerging bits и DSV
Объединяющие биты (merging bits) решают по меньшей мере три важнейших задачи, без которых считывание информации с лазерного диска было бы невозможным.
Во-первых, объединяющие биты препятствуют возникновению конфликтных ситуаций, образующихся на стыке двух EFM-слов одно из которых оканчивается на единицу, а другое с этой самой единицы и начинается (см. рис. 1.110x042). Поскольку две единицы (каждая из которых соответствует переходу с пита (pit)'a в ленд (laend) или наоборот) должны быть разделены по меньшей мере двумя нулями, такая комбинация считается запрещенной, —– считывающее устройство попросту не заметит, что здесь что-то есть (протяженность одного питаpit'a/лендland'a намного меньше диаметра сфокусированного лазерного пятна и потому для уверенного распознавания его длину приходится увеличивать как минимум до 3T, подробнее см. рис. 1.12 0x02C). С другой стороны, если "хвост" одного из EFM-слов состоит из восьми подряд идущих нулей, а другое EMF-слово с тех же самых восьми нулей и начинается, то – на их стыке образуется цепочка из шестнадцати нулей, при чтении которой произойдет ошибка, т. к. по стандарту между двумя соседними единицами должно находиться не более одиннадцати нулей, в противном случае погрешность определения длины такой последовательности становится очень большой. (Ввы не пробовали измерять ученической линейкой расстояние между Ленинградом и Москвой? —– попробуйте и тогда прочувствуете всю суть проблемы целиком). Короче говоря, объединяющие биты выбираются так, чтобы между двумя соседними единицами было не меньше трех, но и не больше одиннадцати нулей.
Рис. 1.11.унок 11 0х042 Объединяющие биты в действии
Во-вторых, объединяющие биты предотвращают возникновение ложных синхрогрупп в неположенном мете. Последовательность бит, образующий синхрогруппу (для справки это —– 100000000001000000000010) может встречаться только в заголовке фрейма и потому служит в качестве своеобразного индикатора его начала.
Когда читающая головка перемещается поперек спиральной дорожки в поисках заданного сектора, ей очевидно каким-то образом приходится выясняить в каком именно месте она находится в данный конкретный момент: в начале, середине фрейма или даже середине EFM-слова. Как это осуществляется? Считывающее устройство пропускает через себя поток цифровых данных до тех пор, пока ему не встретится очередная синхрогруппа. Ага! —– говорит "думательное" устройство CD-ROM привода, – теперь-то я точно знаю, что это действительно начало нового фрейма, а не что ни будь еще! Представляете, какая "каша" началась, если бы "паразитная" синхрогруппа случайно появилась бы в середине фрейма? А ведь ни будь объединяющих битов, то – такое происшествие происходило бы регулярно! Рассмотрим, например, следующие EFM-слова: 10000000000100 и 00000000100100. Если их "склеить" вместе, то образуется паразитная синхрогруппа 100000000001000000000010 (в тексте которая, как было указано ранее, может встречаться только в заголовке фреймаона выделена жирным шрифтом) плюс еще четыре бита 0100 [n2k39] и при попытке чтения такого фрейма произойдет крах. Объединяющие биты, связывающие такие EMF-слова, позволяют всего этого избежать.
Рис. 1.12. унок 12 0x2C Форма высокочастотного сигнала, образующегося при чтении последовательности питов (pits) и лендтов (lands) и его итерпретацияинтерпретация
В третьих… Посмотрите на рис. 1.12
0x02C, —– лазерный диск не имеет никакой иной разметки, кроме самой спиральной дорожки, состоящей из чередующихся питpit'ов и лендlend'ов, при быстрой смене которых возникает высокочастотный, так называемый HF (High Frequency) сигнал, схематически изображенный на графике (б). Этот сигнал имеет большое значение в удержании считывающей головки на спиральной дорожке, поскольку никакого другого способа отличить дорожку от междорожечных "междурядий" и нет. Другая сложность связана с отсутствием опорного сигнала, без которого считывающее устройство не может с уверенностью отличить темные участки поверхности от светлых.
Некоторые источники и в том числе FAQ ( Frequently Asked Questions, часто задаваемые вопросы) по CD-ROM утверждают, что "…если в фотодатчик попадает яркий свет (стандартом предусмотрено, что при полном отражении должно отражаться не менее 70 процентов света-, то "проигрыватель" понимает, что это ровное место на диске ("land"), а если в датчик попадает менее яркий свет, это означает, что в данном месте на диске находится углубление ("pit")". На самом деле, это довольно вольная интерпретация стандарта, который буквально горит следующее "The information contained in the HF signal is extracted in the form of the positions of the crossings of the HF signal with a decision level ID. This decision level ID is the level in the middle of the extreme values of I3" —– "Информация, заключенная в HF-сигнале извлекается в форме позиции, в которой происходит пересечения сигнала с пороговым уровнем ID. Пороговый уровень определяется как половина максимального значения I3 (I3 —– уровень сигнала, соответствующий максимальной частоте смены пи pitт'ов и лендlend'ов)". Величина в 70% взята совсем из другого раздела стандарта, описывающего какой должна быть амплитуда модуляции (Modulation amplitude) должна быть, но ничего не говорящий о том, как именно она модулируется! Как говориться, —– почувствуйте разницу!
А теперь представьте, что произойдет, если на каком-то участке спиральной дорожке окажется значительный избыток питовpit'ов по отношению к лендам,land'ам или наоборот. Вместо переменного высокочастотного сигнала, привод будет считывать постоянный ток высокого или низкого уровня, но в отсутствии опорного сигнала "думающему" устройству привода будет не так-то просто разобраться какой из них какой! Короче говоря, в пределах одного фрейма (сектора?) питpit'ов и лендlend'ов должно быть поровну.
Но за счет чего такое равенство достигается?! Не можем же мы в самом деле писать на диск строго упорядоченные последовательности! К тому же, даже беглого взгляда на таблицу кодировки EMF достаточно чтобы понять, что ноль явно доминирует над единицей и какие бы EMF-последовательности мы только ни записывали, —– "кворума" по единицам мы не наберем… Стоп! Ведь между двоичными битами и питамиpit'ами (лендамиlend'ами) нет прямого соответствия, и двоичный ноль может быть закодирован как питомpit'ом, так и лендlend'ом! Представим себе, что мы записываем на диск EMF-последовательность "10000000100000".
Несмотря на явный избыток двоичных нулей, этот EMF-код содержит приблизительно одинаковое количество питовpit'ов и лендlend'ов (см. рис. 1.13,0x043 a).
Рис. 1.13.унок 13 0х043 ДемостнацияДемонстрация вычисления DSV
Для более точного подсчета этого соотношения ввели специальную величину —– DSV (Digital Sum Value), которая вычисляется следующим образом: изначально DSV равно нулю, но каждый питpit увеличивает его на единицу, а каждый лендlend на единицу уменьшает. В частности, для EFM-последовательности "10000000100000" значение DSV равно двум (см. рис. 1.13, 0х043 а). Именно двум, а не "минус двум", т. к. нас интересует только модуль числа, но не его знак (действительно, если бы эта последовательность начиналась не с питpit'a, а с лендlend'a, то мы бы получили прямо противоположный результат —– восемь "+" и шесть "–").
По стандарту, значение DSV, вычисленное для целого фрейма (сектора?) (сектора) должно находится в интервале от 2 до 10, в противном случае, такой сектор будет читаться с трудом, если вообще будет читаться! А ведь далеко не все EFM-коды могут похвастаться невысоким значением DSV, —– взять, например, хотя бы "00010010000000", DSV которого равно 8. Да, эта величена формально удовлетворяет требованиям стандарта, но если таких последовательностей на диске наберется хотя бы с десяток, значение DSV катастрофически возрастет аж до восьмидесяти!!!
Снижения уровня DSV до предельного допустимого минимума —– это и есть третья задача, с которой справляются объединяющие биты. Как они это делают? Взгляните на рис. 1.13, 0x043.в) —– где за одним EFM-словом, обладающим резко положительным DSV, следует другое EFM-слово с высоким DSV. На самом деле, как уже говорилось ранеевыше, DSV не имеет знака, точнее, действительный знак EFM-слова зависит не от самого этого слова, но от его контекста! Рассмотрим это на примере вырожденной последовательности: "…00000000…".
Поскольку, двоичный ноль соответствует отсутствию изменений в данном месте поверхности диска, то эта последовательность может быть закодирована как восьмью лендlend'ами, так и восемью питpit'ами. Теперь предположим, что мы записываем на диск два EFM-слова, оба каждый из которых имеют значительный избыток лендlend'ов. Можно ли превратить лендlend'ы второго слова в питpit'ы? Да, если хотя бы один из трех объединяющих битов будет равен единице. А единица, как известно, соответствует переходу из лендlend'a в питpit (ну или наоборот), в результате чего второе EFM-слово начнется с пит pit'a и его значение DSV станет резко отрицательным (то есть, попросту говоря она приобретет избыток питpit'ов над лендlend'ами). В результате, EFM-слово с резко положительным DSV будет в той или иной степени компенсировано EFM-словом с резко отрицательным DSV и их общее DSV окажется где-то в районе нуля, – (см. рис. 1.13) 0х043.
Зачем вам, прикладному программисту, знать все эти подробности физического кодирования? А затем, что требования, предъявляемые к объединяющим битам взаимно противоречивы и могут существовать крайне неблагоприятные EFM-последовательности с неустранимо высоким значением DSV. Одна из таких последовательностей и показана на рис. 1.01x024. Смотрите, на конце первого EFM-слова находится восемь нулей, а, поскольку более десяти подряд идущих нулей категорически недопустимо, первый или второй из объединяющих битов обязательно должен быть равен единице. Но в этом случае, следующее EFM-слово приобретает резко отрицательное значение DSV, которое никак не удается скомпенсировать, поскольку между вторым и третьим EFM-словами может присутствовать лишь однако одна-единственная комбинация объединяющих бит "000"., – а Все остальные Любые другие обречены на "провал", т. к. нарушают правило "не менее двух нулей между соседними единицами". В результате, третье EMF-слово также выходит с резко отрицательным DSV-значением и, если мы заполним этой последовательностью весь сектор целиком, его суммарное DSV окажется катастрофически отрицательным!
Рис. 1.14. унок 14 0x024 EFM-последовательность с катострофическикатастрофически низким DSV
Для предотвращения появления подобных последовательностей, все записываемые на диск данные предварительно скремблируются, т. е. преобразуются в псевдослучайную последовательность, близкую по своим характеристикам к "белому шуму". Соответственно, при чтении данных выполняется обратная операция. Однако, при желании скремблер легко обойти! Некоторые защиты от копирования именно так и поступают (см. разд. "Защиты, основанные на «слабых» секторах" главы 9).
Скремблирование
Перед записью на диск, содержимое сектора в обязательном порядке подвергается скремблированию (от английского scrambling —– перемешивание), – т. е. преобразуются в псевдослучайную последовательность, близкую по своим характеристикам к "белому шуму", что исключает непреднамеренное образование регулярных последовательностей с высоким значением DSV —– такие последовательности с точки зрения считывающего устройства считаются крайне неблагоприятными и читаются весьма нестабильно (подробнее об этом см. разд. "Синхрогруппы, объединяющие биты и DSVСинхрогруппы, merging bits и DSV" этой главы).
СкремблируетсяСкремблируются все поля сектора, кроме 12-байтовой синхрогруппы в его начале (если скремблировать и синхрогруппу, то – как потом ее прикажете находить в потоке данных?), что в совокупности составляет 2340 байт данных (см. рис. 1.15 0х045).
Рис. 1.15. унок 15 0х045 Формат сектора с точки зрения скремблера
Скремблирование осуществляется записывающим приводом на аппаратном уровне и совершенно незаметно для программиста. Соответственно, при чтении сектора выполняется обратная процедура, т. е. происходит "скремблирование наоборот" или "де-скремблирование", в процессе чего сектор очищается от "белого шума" и преобразуется к своему исходному виду.
Прозрачность механизма скремблирования создает обманчивое впечатление, что значение его алгоритма совершенно бесполезно для программиста и представляет интерес лишь для разработчиковм аппаратуры. На самом же деле это не так. Коль скоро скремблер для того и придуман, чтобы исключить непреднамеренное появление неблагоприятных последовательностей, то стало быть умение умышленно формировать такие последовательности умышленно
создавать, позволяет создавать диски нечитаемые на аппаратном уровне. Тоже мне новость! —– хмыкнете вы. Создать нечитаемый диск —– много ума не надо. Вжик циркулем по отражающему слою и "родная мама его не узнает".
А еще есть пресс-папье и просто кувалда. Шутки шутками, но весь фокус в том, что благодаря наличию корректирующих кодов можно создать неблагоприятную последовательность, вычислить соответствующие ей корректирующие коды и в слегка измененном виде изменить записать эту последовательность на диск так, чтобы с одной стороны она из неблагоприятной превратилась в благоприятную, а с другой —– при прохождении сквозь декодер Рида-Соломона восстанавливать в свой исходный —– неблагоприятный вид. Попытка скопировать такой диск штатным копировщиком ни к чему хорошему не приведет, т. к. он запишет неблагоприятную последовательность в том виде как она есть, и при ее чтении которой наверняка произойдет ошибка! Захватывающая перспектива, не правда ли? Подробнее об этом приеме мы поговорим в главе 6 , а пока же сосредоточимся на скремблере.
Алгоритм скремблирования, согласно стандарту ECMA-130, выглядит так: "Each bit in the input stream of the scrambler is added modulo 2 to the least significant bit of a maximum length register. The least significant bit of each byte comes first in the input stream. The 15-bit register is of the parallel block synchronized type, and fed back according to polynomial x15 + x + 1. After the Sync of the Sector, the register is pre-set with the value 0000 0000 0000 0001, where the ONE is the least significant bit" ("Каждый бит входного потока скремблера суммируется по модулю 2 с наименее значимым битом максимальной длины регистра. Наименее значимый бит каждого байта, проходит первым во входном потоке. 15-битный регистр представляет собой параллельный блок синхронизированного типа, и заполняется в соответствии с полиномом x15 + x + 1. После прохождения синхрогруппы сектора, – которая не скремблируется, – регистр инициализируется значением 0000.0000.000.0001, где ЕДИНИЦА есть наименее значимый бит" (, см. рис. 1.16 0x44).
Рис. 1.16. унок 16 0х044 Блок схема скремблера
Листинг 1.1. Программная модель скремблера к рис. 1.16
UpdateShiftRegister()
{
int i;
for(i = 0; i < 8;i++)
{
int hibit = ((ShiftRegister & 1)^((ShiftRegister & 2)>>1)) << 15;
ShiftRegister = (hibit | ShiftRegister) >> 1;
}
}
void Scramble()
{
int i;
for (i=12;i<2352;i++)
{
Scrambled[i] = Scrambled[i] ^ (ShiftRegister&0xFF);
UpdateShiftRegister();
}
}
Листинг 1 Программная модель скремблера к рис. 0x044
Непонятно? Мне тоже было непонятно…. во всяком случае до тех пор, пока я не взял в руки дизассемблер и не "распотрошил" копировщик дисков Clone CD. Как известно, программа [n2k40] Clone CD великолепно справляется с защитами, основанными на слабых секторах (см. ….), а раз так, то – он должен содержать в себе свой собственный скремблер.
Действительно, среди функций, экспортируемых динамической библиотекой ElbyECC.dll (входящей в состав Clone CD) обнаруживается однао [n2k41] очень любопытнаяое функцияимя: RawSrcambleSector, дизассемблерный листинг которойго выглядит следующим образом (листинг 1.2).так:
Листинг 1.2. Пример реализации алгоритма скремблирования, позаимствованный из Clone CD
.text:100020E0 RawScrambleSector proc near
.text:100020E0
.text:100020E0 arg_0 = dword ptr 4
.text:100020E0
.text:100020E0 mov eax, [esp+arg_0] ; загружаем переданный аргумент в EAX
.text:100020E4 mov ecx, offset ScrmblrTbl ; в ECX – указатель на ScrmblrTbl
.text:100020E9 add eax, 0Ch ; пропускаем 12 байт синхропослед.
.text:100020EC push esi ; сохраняем ESI
.text:100020ED push edi ; сохраняем EDI
.text:100020EE sub ecx, eax ; вычисляем дельту
.text:100020F0 mov edx, 249h ; 2340 / 4 скремблируемых байт
.text:100020F5
.text:100020F5 loc_100020F5: ; CODE XREF: RawScrambleSector+22vj
.text:100020F5 mov esi, [ecx+eax] ; взять очередной DWORD из таблицы
.text:100020F8 mov edi, [eax] ; взять очередное скремблируемое DWORD
.text:100020FA xor edi, esi ; ксорим
.text:100020FC mov [eax], ed ; записываем результат
.text:100020FE add eax, 4 ; следующий DWORD
.text:10002101 dec edx ; уменьшить счетчик
.text:10002102 jnz short loc_100020F5 ; мотать цикл
.text:10002104 pop edi ; восстанавливаем регистр EDI
.text:10002105 pop esi ; восстанавливаем регистр EDI
.text:10002106 retn ; возвращаемся из функции
.text:10002106 RawScrambleSector endp
Листинг 2 Пример реализации алгоритма скремблирования, позаимствованный из Clone CD
Анализ дизассемблерного листинга показывает, что громоздкой и ресурсоемкой "возне" с полиномом разработчики программы Clone CD[n2k42] предпочли быстрый табличный алгоритм, сводящийся к наложению на скремблируемый сектор псевдослучайной последовательности посредством операции XOR. Фактически, мы получили ни что иное, как "одноразовый блокнот" Вернама, длина "секретного" ключа которого равна длине скремблируемой части сектора (2340 байт для справки). Будучи переведенным на язык высокого уровня данный алгоритм будет выглядеть приблизительно так как это показано в листинге 1.3.:
Листинг 1.3. Пример реализации табличного алгоритма скремблирования на языке Си
RawScrambleSector (char *raw_sector)
{
int a;
DWORD *p;
DWORD *MyScramblerTable = (DWORD *) ScramblerTable;
p = (DWORD*)(raw_sector + 12);
for (a = 0; a < 2340 / 4; a++)
{
p[a] ^= MyScramblerTable[a];
}
}
Листинг 3 Пример реализации табличного алгоритма скремблирования на языке Си
Теперь Оостается разобраться лишь непосредственно с самой псевдослучайной последовательностью, первые восемь членов которой (выдранные[n2k44] полученные с помощью дизассемблераом из того же Clone CD) выглядят следующим образом (листинг 1.4). так:
Листинг 1.4. Первые восемь членов псевдослучайной последовательности, используемой для скремблирования сектора, полученные из копировщика CloneCD
dd 060008001h
dd 01E002800h
dd 006600880h
dd 081FE02A8h
dd 028606080h
dd 0889E1E28h
dd 0AAAE6668h
dd 0E0017FFCh
Листинг 4 Первые восемь членов псевдослучайно последовательности, используемой для скремблирования сектора, выдранные из Clone CD
Вся таблица слишком великавелика, для того чтобы быть приведенной здесь целиком (даже напечатанная самым мелким шрифтом, который только допускают санитарные нормыслужбы, она занимает целую страницу, —– свыше четырех тысяч знаков, которые очень трудно "перебить" с бумаги в компьютер, не допустив при этом ошибок). Поэтому, представляет интерес найти закономерность, которой связаны члены данной последовательности, и воссоздать алгоритм, вычисляющий все члены последовательности по первому из них. Эта маленькая программистская головоломка отнюдь не так сложна, какой кажется поначалу. Да, беглый взгляд на первые восемь членов псевдослучайной последовательности не обнаруживает и намека на их природу —– числа меняются хаотично и сильно смахивают на "пляшущих человечков" над которыми ломал голову Шерлок Холмс. Только частотный анализ в данном случае бесполезен и в "лоб" эту задачу не решить. Но ведь мы начинаем анализ отнюдь не на пустом месте! Во-первых, нам достоверно известно, что скремблирование осуществляется по 16-разрядным словам (разрядность регистра скремблера как раз и составляет 16 бит), а раз так, то и анализировать мы должны именно слова, а не двойные слова.
То, что выполнение операции XOR[n2k45] "ксоренье" идет 32- битными кусками ничего не меняет, ведь – она XOR побитовая операция и потому конкретная разрядность операндов никак не влияет на конечный результат! Во-вторых, анализ закономерностей выгоднее всего проводить на битовом уровне, т. к. именно на битовом уровне эта псевдослучайная последовательность и генерируются.
Следующий Скрипт
(script)[n2k46] , показанный в листинге 1.5, автоматически преобразует все элементы таблицы в 16-разрядные слова, отображаемые в двоичном виде. Запустите дизассемблер IDA Pro, нажиме <F2> и загрузите файл, содержащий этот скрипт. Затем, подогнав курсор к первому элементу таблицы, нажмите <Shift-F2> и введите следующую команду: x2bin(ScreenEA(), ScreenEA()+2340, 2). Комбинация <Ctrl-Enter> (в ранних версиях IDA Pro просто <Enter>) запустит скрипт на выполнение.:
Листинг 1.5. Скрипт на IDA-Си, преобразующий элементы таблицы в двоичный вид
// x_start - начальный адрес для преобразования
// x_len - кол-во байт для преобразования
// x_pow - кол-во байт в одном элементе
static x2bin(x_start, x_end, x_pow)
{
auto a,p;
for(p=x_start;;)
{
// преобразуем в элемент нужной разрядности
if (x_pow == 1) MakeByte(p); else if (x_pow == 2) MakeWord(p); else
if (x_pow == 4) MakeDword(p); else return 0;
// в двоичный вид
OpBinary(p, 0);
// след. элемент
p = p + x_pow;
// выход, если все уже сделано
if (p>x_end) break;
}
}
Листинг 5 Скрипт на IDA-Си, преобразующий элементы таблицы в двоичный вид. Запустите IDA, нажиме <F2> и загрузите файл, содержащий этот скрипт. Затем, подогнав курсор к первому элементу таблицы, нажмите <Shift-F2> и введите следующую команду "x2bin(ScreenEA(), ScreenEA()+2340, 2)".
Комбинация <Ctrl-Enter> ( в ранних версиях IDA просто <Enter>) запустит скрипт на выполнение.
"Обновленная" псевдослучайная последовательность скремблера должна выглядеть так как показано в листинге 1.6 (в немниже приведены 16 ее первых членов).:
Листинг 1.6. Псевдослучайная последовательность, записанная в виде 16-разрядных слов, отображаемых в двоичной нотации
dw 1000000000000001b
dw 0110000000000000b
dw 0010100000000000b
dw 0001111000000000b
dw 0000100010000000b
dw 0000011001100000b
dw 0000001010101000b
dw 1000000111111110b
dw 0110000010000000b
dw 0010100001100000b
dw 0001111000101000b
dw 1000100010011110b
dw 0110011001101000b
dw 1010101010101110b
dw 0111111111111100b
dw 1110000000000001b
Листинг 6
псевдослучайная последовательность, записанная в виде 16-разрядных слов, отображаемых в двоичной нотации
Теперь определенная закономерность сразу же бросается в глаза (вот что значит правильно отформатировать листинг!). Биты каждого последующего элемента смещаются на одну позицию вправо, прижимаясь к логическому "востоку", образуя своеобразную битовую "струю", которая в своем диагональном течении линейно увеличивается в размерах (каждый последующий элемент добавляет ей еще один бит ширины), но на определенном этапе внезапно разбивается на ряд более мелких ручейков, причудливые переплетения которых образуют бессмысленную мешанину. Тем не менее, "физические" принципы, лежащие в основе этого "гидрологического" сооружения, все еще скрыты непроницаемой вуалью тумана и нам не остается ничего иного, кроме как брести наугад, полагаясь лишь на свою интуицию и удачу.
Что мы знаем? Немного… Скремблер выполняет операцию XOR[n2k47] "ксорит" над содержимымое своего внутреннего регистра ис потоком скремблируемых данных и после каждого "отскремблированного" 16-?разрядного слова модифицирует значение этого регистра… но вот как?! Давайте возьмем два соседних элемента нашей псевдослучайной последовательности и попробуем подобрать такую последовательность операций, которая из предыдущего элемента порождала бы следующий.
Возможных вариантов не так уж и много: сдвиг, XOR, AND и OR. Навряд ли создатели CD-?ROM использовали в скремблере что-то еще.
Итак, будет будем оттапливаетсяотталкиваться от того, что "Исаак родил Абрама", то есть от того как скремблер из числа 0110000000000000b скремблер неизвестным науке образом получил число 0010100000000000b. Для компенсации сдвига (который явно имел место) сместим последующий элемент на один бит влево и запишем оба числа одно под другим (по типу сложение по модулю два в столбик):
dw 011000000000000b
dw ???????????????b XOR
-------------------
dw 010100000000000b
Неизвестное слагаемое здесь не найдет только ленивый. Операция XOR над 011000000000000b [n2k48] и XOR 010100000000000b дает…001100000000000b. Постой! Но ведь это же наш исходный член, только сдвинутый на одну позицию вправо! Ну-ка, а как поведет себя следующая пара чисел? Сгорая от нетерпения складываем:
dw 010100000000000b
dw ???????????????b XOR
-------------------
dw 0001111000000000b
Ага, операция XOR над 010100000000000b и XOR 0001111000000000b дает 001010000000000b, значит, мы на верном пути! Наскоро написав простейший скрипит, вычисляющий последующие члены на основе предыдущих мы получим верный результат для всех членов последовательности со второго по седьмой включительно, а вот дальше… Дальше теория и практика разойдутся как большинство супругов. Неожиданно в старшем разряде возникнет единица, которую никто не ждал и которая, естественно, в следующей итерации порождает паразитный "ручеек". Может быть, сдвиг бит в слове происходит циклически, т. е. младший бит "заворачивается" наверх? Но нет, попытка вычисления последующихй членов не подтверждает этой теории.
Провозившись пару часов в попытках найти в доступной мне литературе хоть какую-то информацию о полиномах и особенностях их реализации, я так ни к чему и не пришел. В конце концов, вспоминиав, что "лучше за день долететь, чем за час добежать", я просто распечатал первую сотню членов псевдослучайной последовательности и вручную рассчитал каждый последующий элемент на основе предыдущего данного.
Затем просто отметил все обнаруженные исключения. Выяснилось, что 15-й и 14-й биты (считая от нуля) временами совершают "самопроизвольные" перескоки из нуля в единицу и наоборот. Остальные биты вели себя в полном соответствии с теорией.
Осталось всего ничего —– выяснить при каких условиях происходят эти битовые "мутации". Быстро обнаружилось, что если первый, считая от нуля, бит вычисленного члена последовательности равен единице, то его 15-й бит инвертируется (это действительно легко заметить, особенно если на распечатке). Чуть сложнее далось второе исключение из правил: если нулевой бит вычисленного члена равен единице, то его 15-й и 14-й биты инвертируетсяинвертируются. Соответственно, если и 0-й и 1-й биты содержат по единице, до в силу двойной инверсии 15-го бита реально инвертируется лишь 14-й бит (см. рис. 1.17 0х46). Все! Теперь мы можем рассчитать нашу псевдослучайную последовательность целиком!
Рис. 1.17.унок 17 0x046 Расчет скремблируемой последовательности
Исходный текст программы, генерирующей скремблерную последовательность приведен в листинге 1.7ниже. Здесь и далее в квадратных скобках приведено название файла, находящегося на прилагаемом к книге компакт-диске с соответствующим листингом. Конечно, это далеко не верх оптимизации, но в плане наглядности данный пример весьма удачен.
Листинг 1.7. [/etc/RawScrambler.c] Программа для расчета скремблируемой последовательности
/*---------------------------------------------------------------------------
*
* ГЕНЕРИРУЕТ ПОСЛЕДОВАТЕЛЬНОСТЬ ДЛЯ CD-СКРЕМБЛЕРА
* ===============================================
*
* build 0x001 @ 07.06.2003
----------------------------------------------------------------------------*/
#include <stdio.h>
// контрольный фрагмент истинной последовательности для проверки программы
// -----------------------------------------------------------------------
//0x8001,0x6000,0x2800,0x1e00,0x0880,0x0660,0x02a8,0x81fe,0x6080,0x2860,0x1e28,
//0x889e,0x6668,0xaaae,0x7ffc,0xe001,0x4800,0x3600,0x1680,0x0ee0,0x04c8,0x8356,
//0xe17e,0x48e0,0x3648,0x96b6,0xeef6,0xccc6,0xd552,0x9ffd,0xa801,0x7e00,0x2080,
printf_bin(int a)
{
int b;
for(b = 15; b >= 0; b--) printf("%x",(a & (1<<b))?1:0);printf(" %x\n",a);
}
main()
{
int a, tmp;
int reg = 0x8001; // первый элемент скремблерной последовательности
for(a = 1; a < 1170/* длинна скремблируемой части сектора в словах*/; a++)
{
// вывод на печать
printf_bin(reg);
if ((a % 8) == 0) printf(".............%03d.................\n",a /8);
// сложение по модулю два со свдигом
tmp = reg >> 1; tmp = reg ^ tmp; reg = tmp >> 1;
// обработка полнинома x^15+x+1, что эквив. 1<<15 + 1<<1 + 1<<0
if (reg & 1<<1) reg = reg ^ (1<<15);
if (reg & 1<<0) reg = reg ^ ((1<<15) | (1<<14));
}
}
Сложение и вычитание в полях Галуа
Сложение по модулю два в полях Галуа тождественно вычитанию и реализуется битовой операцией XOR (исключающее ИЛИ). Этот вопрос мы уже обсуждали при изучении полиномиальной арифметики, поэтому не будем лишний раз повторяться, а просто приведем законченный пример программной реализации функции сложения/вычитания (листинг2.12).:
Листинг 21.12. Функция, реализующая сложение/вычитание в полях Галуа
// функция возвращает результат сложения (вычитания)
// двух полиномов a и b по модулю 2
int gf_sum(int a, int b)
{
return a ^ b;
}
Способы разоблачения защитных механизмов
Защита, нуждающаяся в низкоуровневом доступе кс CD, обязательно выдаст себя наличием функций DeviceIoControl и/или SendASPI32Command в таблице импорта. Если же защитный механизм загружает эти функции динамически, "поймать его за хвост" можно установкой точек останова на LoadLibrary/GetProcAddress
[Y148][n2k149] (однако опытные программисты могут отважиться на самостоятельный поиск требуемых им функций в памяти, –— и это отнюдь не такая трудная задача, какой она кажется).
Также в теле программы могут присутствовать строки: "\\.\", "SCSI", "CdRom", "Wnaspi32.dll" и другие. Установив точку останова на первый байт строки, мы сможем мгновенно локализовать защитный код при первом его к ним обращении. Чтобы этого не произошло, разработчики часто шифруют все текстовые строки, однако большинство из них ограничивается примитивной статической шифровкой (которая обычно осуществляется программой ASPack'ом или подобныподобнойми ейму программами), а потому, если дождаться завершения расшифровки и вызвать отладчик после, а не до запуска программы, все текстовые строки предстанут перед нами в прямом виде! Динамическая шифровка намного надежней. В этом случае текстовые строки расшифровываются непосредственно перед их передачей в соответствующую API-функцию, а потом зашифровываются вновь. Но и динамическую шифровку при желании можно преодолеть! Достаточно поставить условную точку останова на функцию CreateFile, которой эти текстовые строки и передаются, всплывая в том и только в том случае, если первые четыре байта имени файла равны "\\.\". Пример ее вызова может выглядеть, например, так: "bpx CreateFileA if (*esp->4=='\\\\.\\')", после чего останется только "пожинать урожай".
Естественно, под "урожаем" понимается, во-первых, имя самого открываемого файла, а точнее –— драйвера (это уже многое что дает), и, во-вторых, возращенный функцией CreateFile дескриптор.
Какие же фокусы используют разработчики, чтобы затруднить анализ драйверов? Ну, вот, например: шифруют текстовую строку с символьным именем устройства, которое создает драйвер при своей загрузке. В результате, хакер точно знает, что защитный код открывает устройство "\\.\MyGoodDriver", но не может быстро установить: какому именно драйверу это имя соответствует. Если же шифровка отсутствует, то задача решается простым контекстным поиском. Вот, например, захотелось нам узнать: какой именно драйвер создает устройство с именем MbMmDp32 –— заходим при помощи файлового менеджера [n2k150] FAR'ом в папку WINNT\System32\Drivers, нажимаем <ALT>+<-F7> и в строку поиска вводим "MbMmDp32", не забыв установить флажок "Use all installed character tables" (в противном случае менджер FAR ничего не найдет, т. к. строка должна задаваться в Unicodeуникоде). Прошуршав некоторое время диском, файловый менеджер FAR выдаст единственно правильный ответ: ASPI32.SYS. Это и есть тот самый драйвер, который нам нужен! А теперь представьте, что строка с именем зашифрована… Если драйвер загружается динамически, то это еще полбеды: просто ставим точку останова на функциюи IoCreareDevice и ждем "всплытия" отладчика. Затем даем команду P RET и по карте загруженных моделей (выдаваемых командой mod) смотрим –— кто "проживает" в данном регионе памяти. С драйверами, загружающимися вместе с самой операционной системой, справиться значительно сложнее и, как правило, отыскивать нужный драйвер приходится методом "тыка". Часто в этом помогает дата создания файла, –— драйвер, устанавливаемый защищенным приложением, должен обычно имееть ту же самую дату создания, что и остальные его файлы. Однако защитный механизм может свободно манипулировать датой создания по своему усмотрению, так что это не очень-то надежный прием. Хороший результат дает сравнение содержимого каталогадиректории WINNT\System32\Drivers до и после инсталляции защищенного приложения, –— очевидно, защита может скрываться только среди вновь появившихся драйверов.
Сводная таблица характеристик различных интерфейсов
В сводной таблице, приведенной далее (табл.1.4.6)ниже, показаны основные характеристики всех ранеевышеописанных методик доступа. Как видно, наибольшее количество очков набрал метод доступа через ASPI, обеспечивающий простой, симпатичный и к тому же системно-независимый интерфейс управления накопителями. Следом на ним идет STPI, основой недостаток которого заключается в том, что он поддерживается лишь операционными системами семейства NT и не работает на "народной" Windows 9x. Неплохой идеей выглядит создание собственного драйвера, –— будучи реализованным под Windows NT и Windows 9x (кстати, WDM-драйвера на уровне исходного кода совместимы с этими двумя системами), обеспечит возможность работы ваших приложений как в NT, так и в Windows 9x.
Таблица 2.1.4.56. Сравнение различныхе методовы доступа в сравнении, неблагоприятные характеристики выделены жирным шрифтом
Характеристика | Метод доступа | ||||||||||||||||||
CDFS | cocked-mode | MSCDEX | ASPI | SPTI | SCSI port | mini port | own driver | IOPM | |||||||||||
Наличие в ОС Windows 9x | –— | –— | + | + | –— | –— | –— | + | н/д | ||||||||||
Наличие в ОС Windows NT | + | + | –— | + | + | + | + | + | + | ||||||||||
Требует права системного администратораа | нет | нет | –— | нет | да | нет | нет | хз* | ** | ||||||||||
Поддерживает CD-DA | да | нет | да | да | да | да | да | да | да | ||||||||||
Поддерживает CD data | да | да | да | да | да | да | да | да | да | ||||||||||
"Сырое" чтение с CD-DA | да | нет | да | да | да | да | да | да | да | ||||||||||
"Сырое" чтение с CD data | нет | нет | да | да | да | да | да | да | да | ||||||||||
Потенциально опасен | нет | нет | нет | да | нет | нет | нет | да | да | ||||||||||
Хорошоо документированиров | да | да | да | да | нет | нет | нет | да | нет | ||||||||||
Легкость использованиять? | да | да | нет | да | да | да | нет | нет | нет |
** — здесь и далее "хз" обозначает "зависит от реализации"
** — установка драйвера требует наличия прав администратора на локальной машине, но вот его последующее использование –— нет
Ячейки с неблагоприятными значениями характеристик выделены черным цветом.
own driver — собственный драйвер.
"Сырые" и "сухие" сектора
IEC 908 —– стандарт на аудио-компакт диски, вышедший в 1982 году в книге с красной обложной (и потому вполне официально называемой "Красной кКнигой" —– Red Book) описывал сектор как логический блок с длиной в 2352 байта, не имеющийх никаких дополнительных полей и представляющийх собой сплошной аудио-поток оцифрованной музыки. Что ж, логично! Сектора всех остальных накопителей (дискет, винчестеров) на логическом уровне устроены совершенно аналогично и различаются разве что длиной (в частности длина сектора гибких/жестких дисков равна 512 байтам).
К сожалению, попытка непосредственного использоватьния аудиоа диска для хранения данных оборачивалась неизменной потерпела неудачуей. Слишком высокая плотность записи в купе с техническим несовершенством механизма чтения привели к тому, что при воспроизведении диска постоянно возникали ошибки, количество которых на 10-секундном участке трека могло доходить до двухсот! Для аудио это считалось вполне нормальноым(что русскому хорошо, то немцу – смерть), поскольку сбойные биты легко выправляются интерполяцией и хотя достоверность воспроизведения аудио-потока при этом уже не гарантируется, человеческое ухо (даже хорошо тренированное!) все равно не замечаететит разницы, а потому увеличение плотности записи в угоду емкости диска выглядело вполне оправданно.ым.
Естественно, для исправления ошибок, возникающих при чтении файлов данных, методика интерполяции абсолютно непригодна и с вероятностью близкой к единице считанный файл окажется безнадежно изуродованным. Для решения этой проблемы пришлось увеличить избыточность записываемой на лазерный диск информации и ввести дополнительные корректирующие коды. По соображениям совместимости с уже имеющимся оборудованием (и производственными мощностями в том числе!) существующий формат хранения информации был полностью сохранен, но к нему добавился еще один уровень абстракции.
Стандарт "Желтой книги" (Yellow Book), вышедший в 1983 году, описывает сектор, как сложную структуру состоящую из 12-байтовой синхропоследовательности, 4-байтового заголовка, 2048-байтовой области данных, 4-байтового поля кода коррекции EDC, 8-байтовой дополнительной вспомогалтельной области (Auxiliary) и 276-байтового поля кода коррекции ECDC (см. рис. 1.0х039).
Рис. 1.9. унок 9 0х039 Сектора различных типов
Естественно, аппаратная начинка привода CD- ROM скрывает все эти подробности и выдает содержимое служебных полей только по специальной команде (которую, кстати говоря, поддерживают не все модели). С программисткой точки зрения 2048 байта пользовательской области данных —– это и есть та минимальная порция информации с которой штатный привод долженможет работать. Кстати, просто Замечательно, что в результате урезания "настоящего" сектора, длина логического сектора оказалась кратной размеру секторов остальных устройств! Так какие проблемы и зачем, срывая уровни абстракции, лезть в такуюкуда вкуда-то вглубь? А вот зачем. – Манипулируя со служебными полями, вы можете ктак создавать диски не копируемые штатными средствами, так и взламывать защитные механизмы, препятствующие несанкционированному копированию.
Итак, Если вы еще не особенно утомились сухой теорией, то совершим еще один рывок и рассмотрим формат сектора для режима MODE 1 (рис. 1.10) (на случай вашего воодушевления хочу сказать, что теория скоро закончится и начнется увлекательный процесс исследования диска под "микроскопом").
Рис. 1.10.унок 10 0х041 Формат сектора для режима MODE 1
q Поле синхронизации (Syncronistion) состоит из следующей последовательности: 00h FFh FFh FFh FFh FFh FFh FFh FFh FFh FFh 00h и служит индикатором начала сектора.;
q Поле заголовка (Header) состоит из четырех байт, первые три из которых занимает физический адрес данного сектора (Sector Address), заданный в формате минуты:секунды:фреймы, а последний, четвертый байт, определяет формат оставшейся части сектора (Mode); если Mode == 0, то остаток сектора (а это без малого 2336 байт) трактуется как User Data и считывается без какой-либо дополнительный обработки; если же Mode == 1, то остаток сектора приобретает вид, изображенный на рис. 1.10 0х041 (как раз о нем мы сейчас и говорим!); естественно, существуют и другие режимы, но в силу их невысокой распространенности подробно останавливаться на этом вопросе мы не будем, а любопытствующих отошлем к соответствующим спецификациям.
q 2048 байт Областьи пользовательских данных (User Data) 2048 байт, которые, как и следуют из их названия области, представляют собой непосредственно хранимую полезную информацию.;
q Четырехбайтовое поле EDC содержит в себе контрольную сумму сектора, вычисляемую по следующему полиному: P(x) = (x16 + x15 + x2 + 1) ´ .(x16 + x2 + 1), причем наименьший значимый бит четности (x0) записывается в наивысший значимый бит контрольной суммы, а сам подсчет контрольной суммы осуществляется с наименьшего значимого бита данных.
q Вспомогательное поле Intermediate -поле хранит в себе восемь байт нулей и реально никак не , если честно, я не совсем понимаю как оно используется (по-видимому, оно не используется вообще, во всяком случае, защитными механизмами —– точно!).;
q Поля P-parity и Q-parity с длиной в 172- и 104-байта соответственно, вмещают в себя так называемыеющие корректирующие коды Рида-Соломона (Reed-Solomon), математический принцип действия которых, равно как и сам алгоритм их генерации, здесь излагать было бы слишком утомительно, да и не нужно, поскольку все это уже подробно описано в стандарте ECMA-130. Тем более, что для подавляющего большинства задач умения вычислять корректирующие коды и не требуется, —– чаще всего их просто "забивают" всякой бессмысленной чепухой, имитируя тем самым неисправимую ошибку чтения сектора (то есть, попросту говоря, эмулируя физические дефекты поверхности за счет создания логически не читающихся секторов —– для взлома защит, привязывающихся к физическим дефектам —– это самое то!);
формат данных Q-подканала
Рис. 1.8. унок 8 0x021 Формат данных Q-подканала[Y33] [n2k34]
Поле Control определяет содержимое трека (аудио или данные), количество аудиоканалов (стерео или квадро), а так же указывает разрешается ли копировать данные или нет. В частности, последовательность "0110" обозначает, что в пользовательской части сектора (user-data) записаны цифровые данные и их копирование не возбраняется. Напротив, последовательность "0100" запрещает копирование данных с диска. Другими словами, если третийпервый слева, считая от нуля, т. е. 2 бит установлен, то копирование разрешено и, соответственно, запрещено, если сброшеннаоборот. Забавно, но большинство пишущих приводовсцов всегда сбрасывает этот бит в ноль, даже если на диск записываются файлы, созданные самим пользователем. Впрочем, копировщики (в том числе и штатные) целиком и полностью игнорируют эти нелепые запреты, а потому конечный пользователь даже не догадываться о том, каких проблем он избежал!
Поле q-Mode определяет формат представления данных в поле q-Data и для подавляющего большинства CD-ROM дисков оно равно единице.
Поле q-Data в режиме q-Mode == Mode 1 состоит из девяти однобайтовых полей, содержащих информацию о секторе (остальные режимы в силу их экзотичности не рассматриваются):
q TNO (Track Number) —– содержит в себе номер текущего трека (см. "трек"), принимающий значения от 01 до 99; магическое число 0xAA указывает на трек Lead-Out(см. "lead-out");
q INDEX —– содержит в себе индекс текущей секции внутри текущего трека: 00 – указывает на паузу, значения от 01 до 99 идентифицируют секцию с полезными данными; однако в настоящее время эта возможность не используется и индекс секции всегда равен либо нулю (audio-pause), либо единице (actual data); индекс трека Lead-Out должен быть равен нулю;
q MIN, SEC, FRAC —– время, проигрывания сектора от начала текущего трека (минуты: секунды: фреймы секунды [Y35] [n2k36] соответственно), так же называемое относительным временем проигрывания;
q ZERO —– это поле должно всегда быть равно нулю;
q A-MIN, A-SEC, A-FRAC —– время проигрывания диска от начала области данных (минуты: секунды: фреймы секунды соответственно), так же называемое абсолютным временем проигрывания.;
Поле CRC содержит контрольную сумму содержимого Q-канала подкода и вычисляется по следующему полиному G(x) == x16+x12+x5+1;
Так как же все-таки скопировать такой диск?
Конечно, с помощью "Добермана Пинчера[Y167] [n2k168] " ("Pinch of File") (или любого другого блочного копировщика файлов), HIEW[Y169] [n2k170] , двух образов защищенного диска (один –— с первой сессией –— от Clone CDCloneCD, другой –— со второй сессией –— от Alcohol 120%) и еще "чьей-то матери" мы можем воссоздать идентичную копию оригинального диска, путем их совокупного объединения, но… это будет как-то не по-хакерски, да и вообще некрасиво.
Чтобы не писать свою собственную программу "прожига" диска ограничимся использованием Clone CDCloneCD. При условии, что подсунутый ему образ диска запечатлен правильно, Clone CDCloneCD обычно справляется с "прожигом" на ура.
Итак, у нас есть более и менее верный файл IMAGE.CCD, содержащий TOC (его можно позаимствовать от программы Alcohol 120%), но недостает файла-образа IMAGE.IMG. Попробуем его получить? Будем отталкиваться от того, что LBA-адреса всех секторов диска пронумерованы последовательно, включая области, занятые Lead-inLead-In/Lead-outLead-Out и прочими служебными записямибарахлом. Разумеется, непосредственное чтение служебных областей диска на сектором уровне невозможно, но… именно на этом мы и собираемся сыграть! Последовательно читая диск с первого по последний сектор, мы обнаружим, что сектора с LBA-адресами с 0 по 2 055 сектор включительно читаются без каких-либо проблем, после чего наступает "сумеречная зона" не читающихся секторов, протянувшаяся вплоть до 13 307 сектора. Здесь сектора либо совсем не читаются, либо возвращаются в сильно "мутированном" виде, легко опознаваемым по отсутствию правильной синхро-последовательности в их заголовке. Наконец, с адреса 13 308 чтение вновь продолжается без каких-либо проблем.
Судя по всему, мы имеет дело с двухсессионным диском и "сумеречная зона" между сессиями есть ни что иное как области Lead-outLead-Out/Lead-inLead-In.
Накинув два сектора на область Post-gap (при условии, что она записана с соблюдением стандарта), получаем, что LBA-адрес последнего значимого сектора первой сессии составляет: 2 057 или, в пересчете на абсолютные единицы –— 00 минут, 29 секунд и еще 32 фрейма. Соответственно, LBA-адрес первого сектора второй сессии равен: 13 308 + 150 (Pre-gap) == 13 458 или 3 минуты, 1 секунда, 33 фрейма. Конечно, если исследуемый диск содержит большое количество ошибок, то его анализ значительно усложняется, т. к. физические дефекты на сектором уровне могут выглядеть точно так же, как области Lead-inLead-In/Lead-outLead-Out, конечно, при том условии, что дефективные области имеют соответствующую протяженность –— а это вряд ли.
Отбросив сектора, расположенные в зонах Pre-gap и Post-gap (т. е. 150 секторов от конца первой читаемой области и ровно столько же от начала следующей), мы должны объединить их в один файл, используя для этой цели любой файловый копировщик (например, штатную команду MS-DOS copy file_1 /b + file_2 image.img). Остается прочитать "сырой" TOC SCSI/ATAPI командой READ TOC (opcode: 43h, format: 2h) и записать его в файл IMAGE.CCD в соответствии с синтаксисом Clone CDCloneCD. Как альтернативный вариант –— можно воспользоваться CCD-файлом, сформированным программой Alcohol 120%, предварительно скорректировав Pre-gap Mode (как уже сказано выше, Alcohol 120% определил его неправильно, перепутав Mode 1 с Mode 2). Согласно стандарту, режим сектора задается пятнадцатым, считая от нуля, байтом его заголовка. Если этот байт равен одному (что, собственно, и наблюдается в нашем случае), то и Mode сектора будет 1, но не 2.
При условии, что все сделано правильно, после записи собственноручно сформированного образа диска, мы получаем практически идентичный оригинал. Просто? Да проще простого! И написать автоматический копировщик, автоматизирующий наш труд, можно буквально за несколько часов! Если чтение "сырых" секторов с диска представляет для вас проблему, воспользуйтесь исходными текстами утилит ASPI32.raw/SPTI.raw как раз такое чтение и осуществляющих.
Так что искажение TOC –— не очень-то надежный прием защиты от копирования, как ни крути. Правда, от обычных пользователей, вооруженных программой Clone CDCloneCD или Alcohol 120% он все-таки спасает, а больше от защиты зачастую и не требуется.
Техника восстановления данных с лазерных дисков или практическое знакомство с сессиями
…такой объем информации можно уничтожить в один миг разве что динамитом, потому что существуют дублирующие системы и у скорости обработки есть предел
Джон Варли "Нажмите ENTER"
Записываемые и перезаписываемые лазерные диски представляют собой идеальное средство для резервирования информации умеренных объемов (а всякий уважающией себя программист обязательно должен заботиться о периодическом резервировании вверенной ему информации!). К сожалению, никакая работа без ошибок не обходится (что поделаешь –— ERRARE HUMANUM EST — человеку свойственно ошибаться (лат.) –— ERRARE HUMANUM EST как говорили древние) и ошибочное удаление файлов с дисков CD-R и CD-R/CD-RW дисков[n2k15] , равно как и непредумышленная очистка последних –— хотя бы однажды да случается (на самом же деле, как показывает практика, с этим явлением приходится сталкиваться достаточно частос этим далеко не однажды[n2k16] ).
Насколько известно автору, утилит, предназначенных для восстановления информации с лазерных дисков, до сих пор не разработано (во всяком случае, они не были широко представлены на рынке), поэтому, восстановлением "запоротых" дисков в подавляющем большинстве случаев приходится заниматься самостоятельно. О том, как именно это сделать и рассказывает настоящая глава.
Трек с данными, маскирующийся под аудио
Чем отличаются аудиотреки от треков с данными? И что произойдет если трек с данными пометить как аудиотрек? На первый взгляд здесь не случиться ничего интересного и подопытный трек будет спокойно читаться командой READCD с той лишь разницей, что автоматическая коррекция ошибок Q- и P-уровней уже не выполняется приводом, но сбойные сектора вполне поддаются восстановлению вручную. Защитный механизм, знающий истинный формат считываемых секторов такую коррекцию сможет осуществить без труда, а вот программы-копировщики — нет. А потому при многократном перекопировании диска, количество ошибок чтения будет неуклонно накапливаться и на каком-то этапе корректирующих способностей кодов Рида-Соломона окажется недостаточно и очередная по счету копия откажет в работе. Однако, при нынешнем качестве оптических носителей при бережном обращении с ними количество ошибок Q- и P-уровней крайне невелико и копии первых трех поколений гарантированно будут читаться даже раздолбанными "узкоглазыми" приводами. Так что хорошей защиты мы из этого не "сварим".
Подобные рассуждения типичны для специалиста средней руки, считающего что треки с данными отличаются от аудиотреков одним лишь битом поля ADR/Control (и это третий, считая от нуля бит), и в тоже время удивляющегося почему его привод "грабит" аудио-треки не вполне корректно. Хорошенько порывшись в спецификациях и отдизассемблировав пару тройку микропрограмных прошивок, мастера своего дела приходят к выводу, что в обработке аудиотреков и треков с данными присутсвует по меньшей мере пять концептуальных различный. Вот они.
q
q Точное позиционирование на заданный аудиосектор невозможно в принципе, поскольку адреса секторов, "проплывающих" в данный момент над оптической головкой, хранятся не в самом секторе, а в Q-канале подкода, "размазанном" вдоль спиральной дорожки. Упрощенно говоря, субканальные данные из 748 фреймов или 8 секторов (хотя, их правильное название — блоки) объединяются в пакеты, причем каждый такой пакет содержит в себе по четыре точки отсчета, что в идеальном случае соответствует погрешности в ±1 сектор, а на практике погрешность может достигать и большиших величин.
Стандарт устанавливает предельно допустимый уровень погрешности в ±1 сек (± 75 секторов), однако некоторые приводы показывают значительно худший результат, так, например, мой TEAC "уплывает" вперед аж на ~500 секторов! И у нас нет никакой возможности прочитать сектор с заранее заданным адресом! Такое положение дел, вполне удовлетворяющее запросы аудиотреков, для треков с данными категорически неприемлимо и потому они вынуждены оснащать свой заголовок специальным полем, содержащим их собственный адрес. Грубая наводка на сектор осуществляется по субканальным данным, а точная — по их заголовку, в результате чего привод всегда считывает именно тот сектор данных, который и был запрошен.
q Понятие "сектора" к аудиотрекам вообще не применимо. В них нет секторов, но есть так называемые блоки (blocks), которые состоят из последовательности непронумерованных фреймов, причем границы блоков категорически не фиксированы и блок имеет право начинаться с любого понравившегося приводу фрейма, расположенного в окрестностях позиционирования. Поэтому, при чтении трека с данными в режиме аудио мы не можем быть уверенными, что прочитанные сектора будет начинаться с "головы", а не "хвоста".
q Сектора с данными скремблируются, а аудиосектора — нет, причем необходимость в скремблировании определяется не типом текущего трека, а наличием синхропоследовательности и правильным Mode в его заголовке. Трек с данными, записанный как аудиотрек принудительно скремблируется при записи (что "гробит" Mode), но не дескремблируется при его чтении. Другими словами, с диска читается совсем не то, что на него писалось.
q Некоторые приводы (например, Plextor) осуществляют принудительную аудиокоррекцию записываемых данных, мотивируя это своим стремлением обеспечить более приятное звучание. Неудивительно, что попытка записи трека с данными в режиме аудио приводит к его полному уничтожению.
q Как уже говорилось ранее, аудиотреки не содержат Q- и P-кодов коррекции и потому привод и не корректирует их, что ведет к накоплению ошибок, однако, копии первых трех-пяти поколений замечательно читаются и без корректирующих кодов.
Так, теперь понятно, почему задача точного извлечения аудиотреков такая сложная, если не сказать — невозможная. Но как это обстоятельство можно использовать на практике? Давайте запишем трек с данными как аудиотрек и немного поэкспериментируем с ним. Это поможет нам ответить на поставленный выше вопрос.
Разумеется, непосредственно этого не сделать и шатаные программы "прожига" нашего хитрого замысла просто не поймут, в лучшем случае выдав что-то наподобие "Illegal mode for this track", а в худшем просто обозвав нас дураками. Что ж, придется идти обходным путем. Используя Ahead Nero, Stomp Record Now! или любую другую программу аналогичного назначения, создадим обыкновенный лазерный диск, содержащий один или несколько треков данных. Затем, используя копировщик Clone CDCloneCD (Alcohol 120%Алкоголь) снимем с диска образ и слегка отредактируем его CCD-файл. Все, что нам надо — найти Entry, чей указательPoint равен номеру нашего трека и изменить содержимое поля Control на ноль или два (аудитрек без защиты авторских прав и аудиотрек с защитой оных соотвественно). Так же следует поменять MODE трека c одного на ноль (см. секцию [TRACK] листинга 6.58).
Листинг 6.58. Создание образа защищенного диска
[Entry 11] |
[Entry 11] |
[TRACK 1] |
[TRACK 1] |
Session=2 |
Session=2 |
MODE=1 |
MODE=1 |
Point=0x02 |
Point=0x02 |
||
ADR=0x01 |
ADR=0x01 |
[TRACK 2] |
[TRACK 2] |
Control=0x04 |
Control=0x02 |
MODE=1 |
MODE=0 |
TrackNo=0 |
TrackNo=0 |
INDEX 1=0 |
INDEX 1=0 |
AMin=0 |
AMin=0 |
||
ASec=0 |
ASec=0 |
||
AFrame=0 |
AFrame=0 |
||
ALBA=-150 |
ALBA=-150 |
||
Zero=0 |
Zero=0 |
||
PMin=3 |
PMin=3 |
||
PSec=1 |
PSec=1 |
||
PFrame=33 |
PFrame=33 |
||
PLBA=13458 |
PLBA=13458 |
[Entry 11] [Entry 11] [TRACK 1] [TRACK 1]
Session=2 Session=2 MODE=1 MODE=1
Point=0x02 Point=0x02
ADR=0x01 ADR=0x01 [TRACK 2] [TRACK 2]
Control=0x04 Control=0x02 MODE=1 MODE=0
TrackNo=0 TrackNo=0 INDEX 1=0 INDEX 1=0
AMin=0 AMin=0
ASec=0 ASec=0
AFrame=0 AFrame=0
ALBA=-150 ALBA=-150
Zero=0 Zero=0
PMin=3 PMin=3
PSec=1 PSec=1
PFrame=33 PFrame=33
PLBA=13458 PLBA=13458
Листинг 50 создание образа защищенного диска
Субканальные данные корректировать не обязательно. Напротив, лучше оставить их в своем неизменном виде. Это запретит проигрывание данного трека в аудирежиме и избавит вас и ваших пользователей от тяжелого психического шока, вызыванного прослушиванием "симфонии данных", замаскированных под аудио. Что произойдет, если, скорректировав субканальные данные, проиграть "аудиотрек" на бытовом или компьютерном приводе? Да в общем-то ничего ужасно, но… Вы когда-нибудь загружали программы с магнитофонной ленты в персональный компьютер типа "Правец-8Д", "Электроника-БК" или "ZX-Spectrum"? А динамик модема во время передачи данных включали? Если да, тогда вы имеете представление о характере звука, который обрушится на ничего не подозревающих пользователей, вставивших такой диск в привод.
Так и заикой недолго стать, особенно если регулятор громкости выведен на максимум. Некоторые специалисты утверждают, что существует риск вывода усилителя и колонок из строя, однако, на мой взгляд, подобные слухи беспочвены и бессмысленны. Ни предельно доступимый уровень выходного сигнала, ни предельно достустимый спектр частот при этом не может быть превышен, и правильно спроектированный усилитель перенесет это испытание без особого вреда для себя. Тоже самое относится и колонкам.
Тем не менее, если вы используете режим "цифрового воспроизведения" треков, штатно поддерживаемый Windows 2000, то независимо от содежимого каналов подкода, треки с данными, помечанные как аудиотреки, воспроизводиться все-таки будут! А некоторые приводы (такие, например, как NEC) воспроизведут эти треки и в аудиорежиме. Что ж, пусть воспроизводят! А на все недовольные вопли ошарашенных пользователей мы ответим, что программа загружает себя через аудиотракт, — как и в былые времена. Ну чем не лекарство от ностальгии?
Рис. 6.23. унок 18 0x106 Данные, представленные как аудио[Y179]
А теперь прочитаем образ диска с аудиотреком и сличим его содержимое с исходным (см. рис. 6.23). Уверяю вас, результат превзойдет все ваши ожидания! Начнем с побайтового сравнения IMG-файлов, несущих на своих плечах данные основного канала. Используя любой компаратор (хоть fc.exe из штатной поставки Windows, хоть мой любимый c2u от Профессора Нимнула (Professor Nimnul)), запустим его приблизительно следующим образом: FC.EXE IMAGE.IMG IMAGE_T.IMG /B > IMAGE.DIF, где IMAGE.IMG — образ, снятый с "нормального" диска, а IMAGE_T.IMG, — образ, снятый с диска с "аудиотреком".
Через несколько минут (утилита fc.exe работает до "ужжжаса" медленно, не то, что c2u) на вашем жеском диске образуется файл IMAGE.DIF с размером в добрые 50 Мбайт. Стало быть какие-то отличия между "копией" и "оригиналом" все-таки есть и этих отличий не просто много, а очень-очень много!
Листинг 6.59. Фрагмент оригинального диска
0049D2B0: 00 FF FF FF FF FF FF FF ¦ FF FF FF 00 00 29 32 01 )2O
0049D2C0: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
…
0049DBE0: 00 FF FF FF FF FF FF FF ¦ FF FF FF 00 03 01 33 01 ¦O3O
0049DBF0: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
0049DC00: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
0049DC10: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
0049DC20: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
0049DC30: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
0049DC40: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
0049DC50: 00 00 00 00 00 00 00 00 ¦ 00 00 00 00 00 00 00 00
Трек с нестандартным номером
Внедрение в TOC фиктивных треков с нестандартными номерами обеспечивает создание стойкой, элегантной и в высшей степени неконфликтной защиты. Но обо всем по порядку. По стандарту номера нормальных треков могут принимать значаения в диапазоне от 1 до 99 (4Bh) включительно. Трек номер 0 принадлежит вводной области диска и никогда явным образом не присутствует в TOC (см. разд. "Диск с нулевым треком" этой главы). Трек номер AAh (170) принадлежит выводной областиью диска и так же никогда не присутствует в TOC. Треки с номерами A0h, A1h, A2h, B0h, B1h, B2h, B3h, B4h, C1h и C2h используются для хранения служебных данных и интерпретируются особенным образом, специфичным для каждого номера. В частности, стартовый адрес трека A1h содержит в себе номер последнего трека данной сессии, хранящийся в поле PMin.
Приводу запрещается обрабатывать треки с нестандартными ему номерами и такие треки по стандарту должны игнорироваться. Создавая трек с нестандартным номером мы можем быть абсолютно уверенны в том, что он не сорвет крышубудет "по зубам" приводу и ни в коей мере не нарушит читабемльность диска. Правда, существует определенная вероятность, что с течением времени выбранный нами нестандартный номер трека перейдет в разряд стандартных и тогда поведение привода станет непредсказуемым. Однако степень риска крайне невелика —– формат носителей CD-ROM носителей не меняется каждый день и уже содержит в себе все необходимое. Навряд ли в него будут добавляться новые point'ыуказатели, да еще и совпадающие с нашим. Так что предлагаемая защита вполне соотвествует "букве" и "духу" стандарта и в категорию "защит, основанных на искаженном TOC'е" она попала число случайно, —– так уж был скомпонован материал настоящей книги.
Но если защищенный диск вполне корректно обрабатывается приводом, то в чем же трудность его копирования? Дело в том, что о сущестовании нестандартных треков копировщику не так-то просто узнать. Даже при чтении TOC'a в "сыром" виде (команда READ TOC формат 2) привод не возвращает атрибуты нестандартных номеров треков, так что эта "сырость" весьма относительна и дотянуться до них можно лишь на субканальном уровне, читая содержимое области Lead-inLead-In области командами SEEK или /READ SUBCHANNEL (при этом треки с нестандартными номерами должны располагаться не в первой сессии, т. к.
ее область Lead-inLead- In область на интерфейсном уровне недоступна).
Защите будет достаточно лишь просканировать вводную область соответствующей сессии на предмет поиска "своих" номеров треков и, если окажется, что их там нет, то – мы имеем дело не с оригиналом, ано с копией. Читать субканальные данные вводных областей второй и всех последующих сессий умеют все приводы, правда некоторые из них делают это через то местоочень коряво и "криво", куда Макар телят не гонял. В частности нестандартный номер X может быть замещен номером 64h + (X – A0h). Так например, номер ABh интерпретируется приводом NEC как 6Fh. Следовательно, на выбор нестандартных номеров наложены определенные ограничения и по соображениям совместимости и неконфликности номера 65h—…9Fh лучше не выбирать, т. к. в этом случае привод может ошибочно интерпретировать нестандартный номер трека как стандартный (допустим, мы выбрали номер 66h, тогда 64h + 66h – A0h == 2Ah или 42 в десятичной системе исчисления, трек с номером 42 —– вполне стандартый трек).
ХорошоОК, приступаем к нашим традиционным экспериментам. Как говорится "не откадывай на завтра то, что можно выпить сегодня" (а хачинье взлом лазерных дисков —– лучший повод для оправдания количества выпитого пива. Кстати о пиве – если из под лежачего хакера что-то и течет, то это отнюдь не пиво. Впрочем, не будем отклоняться от темы обсуждения и вернемся к нашим "баранам").
Используя CCD-образ, оставшийся от прошлых экспериментов с нулевым треком, изменим строку "Point = 0x00" на "Point = 0xAB" (естественно, вместо 0xAB вы можете выбрать любое другое значение, но с учетом требований, предъявляемых к нестандартным номерам треков, о которых уже рассказывалось ранеевыше). Что? У вас нет CCD-образа? Тогда сделайте следующее:
10. С помощью своей любимой програмы "прожига" подготовьте лазерный диск, содержащий две сессии и два трека —– по одному треку в каждой.;
11. Снимите образ диска в Clone CDCloneCD- формате ( для чего пригодиться не только сам копировщик Clone CDCloneCD, но и любой другой совместимый с ним копировщик, например, Alcohol 120%Алкоголь).;
12. Откройте CCD-файл в текстовом редакторе типа "Блокнот" или нажмите <F4>, находясь в файловом менеджере FAR'e.;
13. Увеличьте значение поля TocEntries на единицу.;
14. Следом за треком два (Session = 2, Point = 2) добавьте еще одно Entry, чей указательPoint был бы равен ABh или любому другому выбранному вами значению. Содержимое остальных полей данного Entry не критично и вы вполне можете использовать их для хранения ключевой информации.;
15. Увелитьте значения всех последующих Entry на единицу, чтобы сохранить переемственность принятой системы нумерации.;
16. Нажмите <F2> или <Ctrl> +– <S> для сохранения изменений в файле.
(Внимание!
"Блокнот" под Windows 9x не понимает клавиатурной комбинации <Ctrl>+<S>, а вот его собрат под Windows NT/2000 —– понимает.
Как бы там ни было, вы должны получить приблизительно следующий результат (листинг 6.50) (по соображениям экономии места ниже приведен лишь фрагмент последнего).:
Листинг 6.50. Нестандартный номер трека в CCD-файле
[Entry 12]
Session=2
Point=0xAB
ADR=0x01
Control=0x04
TrackNo=0
AMin=0
ASec=0
AFrame=0
ALBA=-150
Zero=0
PMin=3
PSec=1
PFrame=66
PLBA=13458
Листинг 42 нестандартный номер трека в CCD-файле
Записав полученный образ на диск, убедитесь, что он нормально читается всеми, доступными вам приводами и ни коем образом ни с чем не конфликтует. А теперь считайте содержимое TOC и посмотрите: присутствует ли там "наш" нестандартный номер треака (track) или нет (листинг 6.51).:
Листинг 6.51. Содержимое TOC в "сыром" виде с отсутствующим нестандартным треком
KPNC$F:\.PHCK3\src\etc\RAW.CD.READ>CD_RAW_TOC_READ.exe 1.1
RAW TOC READER by Kris Kaspersky
* * * TOC * * *
session number
| ADR/control
| | TNO
| | | point
| | | |
AM:AS:AF
| | | | | | | zero
| | | | | | | | PM:PS:PF
01 14 00 A0
00 00 00 00 01 00 00
01 14 00 A1
00 00 00 00 01 00 00
01 14 00 A2
00 00 00 00 00 1D 21
01 14 00 01
00 00 00 00 00 02 00
01 54 00 B0
02 3B 21 03 16 0E 22
01 54 00 C0
A2 C8 E0 00 61 1B 15
02 14 00 A0
00 00 00 00 02 00 00
02 14 00 A1
00 00 00 00 02 00 00
02 14 00 A2
00 00 00 00 03 18 17
02 14 00 02
00 00 00 00 03 01 21
02 54 00 B0
04 36 17 01 16 0E 22
Листинг 43 содержимое TOC'а в сыром виде с отсутствующим нестандартным треком
Хм! Такое впечатление, что нестандартный номер трека вообще не записался на диск. На самом же деле это предположение неверно. Записаться-то, он записался, ано вот просчитался —– едва ли. Привод просто отказался возвращать атрибуты нестандартного номера трека, делая вид, что как будто его и не было. Причем, это отнюдь не дефект какой-то одной отдельно взятой модели —– так ведут себя все приводы! (во всяком случае —– все, доступные мне, —– точно).
А теперь посмотрим что даст чтение вводной области второй сессии на субканальном уровне. Естественно, чтобы прочитать субканальные данные вводной области мы должны знать ее стартовый адрес. Как его определить? Да очень просто. Открываем CCD-файл и ищем Entry, чей Session равен 1, а Point – — A2h (она содержит стартовый адрес выводной области первой сессии) (листинг 6.52).:
Листинг 6.52. Адрес выводной области первой сессии
[Entry 2]
Session=1
Point=0xa2
ADR=0x01
Control=0x04
TrackNo=0
AMin=0
ASec=0
AFrame=0
ALBA=-150
Zero=0
PMin=0
PSec=29
PFrame=33
PLBA=2058
Листинг 44 адрес выводной области первой сессии
Легко видеть, что абсолютный адрес выводной области первой сессии составлет 00:29:33 (или 2058 в LBA-формате). Увеличив его на длину выводной области (30 сек. или 2250 в LBA-формате) мы получим абсолютный адрес вводной области второй сессии, который в данном случае равен 00:59:33 или 4308 в LBA-формате.
Что ж! Остается всего лишь запустить утилиту seek_and_Q.exe, см. прилагающийся к книге компакт-диск, и проанализировать поток цифр, возникающий на экране (листинг 6.53).:
Листинг 6.53. Содержимое TOC прочитанное на субканальном уровне в нестандартным номером трека внутри, прочитанное на приводе TEAC
KPNC$F:\.PHCK3\src\etc\RAW.CD.READ>seek_and_Q.exe 1.1 4308 4444
seek CD-ROM & read Q-subcode by KK
LBA - 10D4:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 6E
…
LBA - 10E9:00 15 00 0C 01 14 00 02 00 00 34 92 00 00 11 7F
LBA - 10EA:00 15 00 0C 01 14 00 AB 00 00 34 B3 00 00 11 85
LBA - 10EB:00 15 00 0C 01 14 00 02 00 00 34 92 00 00 11 81
LBA - 10EC:00 15 00 0C 01 14 00 AB 00 00 34 B3 00 00 11 85
LBA - 10ED:00 15 00 0C 01 14 00 AB 00 00 34 B3 00 00 11 85
LBA - 10EE:00 15 00 0C 01 14 00 AB 00 00 34 B3 00 00 11 86
LBA - 10EF:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 8B
LBA - 10F0:00 15 00 0C 01 14 00 AB 00 00 34 B3 00 00 11 86
LBA - 10F1:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 8B
LBA - 10F2:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 8B
Листинг 45 содержимое TOC'а прочитанное на субканальном уровне в нестандартным номером трека внутри, прочитанное на приводе TEAC
Оторвать хвост ТигруУра! Это получилось!!! Нестандартные номера треков все-таки присутствуют в TOC'е! Ну-ка, посмотрим, как поведут себя другие модели приводов, например, крайне "капризный" в этом отношении привод NEC (листинг 6.54).?
Листинг 6.54. Содержимое TOC прочитанное на субканальном уровне в нестандартным номером трека внутри, прочитанное на приводе NEC
LBA - 1188:00 15 00 0C 01 14 00 66 00 00 11 7A 00 00 3B 45
LBA - 1189:00 15 00 0C 01 14 00 6F 00 00 11 86 00 00 34 B3
LBA - 118A:00 15 00 0C 01 14 00 6F 00 00 11 87 00 00 34 B3
LBA - 118B:00 15 00 0C 01 14 00 6F 00 00 11 85 00 00 34 B3
LBA - 118C:00 15 00 0C 01 14 00 6F 00 00 11 87 00 00 34 B3
LBA - 118D:00 15 00 0C 01 14 00 6F 00 00 11 85 00 00 34 B3
LBA - 118E: 00 15 00 0C 01 14 00 02 00 00 11 80 00 00 34 92
Листинг 46 содержимое TOC'а прочитанное на субканальном уровне в нестандартным номером трека внутри, прочитанное на приводе NEC
Ага! Трека с номером ABh здесь все-таки не оказывается, зато присутствует трек с номером 6Fh, что с учетом преобразования по ранеевыше приведенной формуле дает: A0h + 6Fh -? 64h == ABh, —– как раз то, что нам нужно.
Копирование защищенного диска копировщиком Alcohol 120% Алкоголем и/или Clone CDCloneCD протекает вполне нормально (никто не "виснет", ничего не взрывается и не дымит), однако, и нестандартных номеров треков в субканальных данных второй сессии уже не оказывается, и по этим абсолютным адресам содержатся атрибуты уже совсем других треков, смотрите сами (листинг 6.55).:
Листинг 6.55. Копия диска уже не содержит нестандартного трека
LBA - 10EB:00 15 00 0C 01 14 00 02 00 00 34 92 00 00 11 7F
LBA - 10EC:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 86
LBA - 10ED:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 86
LBA - 10EE:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 86
LBA - 10EF:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 87
Листинг 47 копия диска уже не содержит нестандартного трека
Действуя по описанной ранеевыше методике, защитный механизм сможет без труда отличить оригинальный диск от его подлой копии, причем тиражирование оригинальных дисков элементарным образом осуществляется на штатном оборудовании (обычный рекордер плюс копировщик Clone CDCloneCD или/ Alcohol 120% Алкоголь —– вот и все, что вам нужно). Ничуть не с меньшим успехом защита может использоваться и в производстве штампованных CD, но это уже тема другого разговора.
Памятуя о пословице "чем меньше женщину мы любим —– тем больше времени мы спим" перейдем от любви к практике. То есть —– ко взлому. Несмотря на высокую стойкость защиты перед существующими на момент написания этих строк автоматическими копировщиками, скопировать оригинальный диск вполне по силам опытному хакеру средней руки.
Достаточно лишь запустить анализатор субканальных данных (для этих целей вполне подойдет утилита seek_and_Q) и прочитать "сырой" TOC второй и всех последующих сессий диска (если они есть). Там, среди прочей информации, мы обнаружим уже знакомый нам нестандартный номер трека (листинг 6.56).
Листинг 6.56. Идентификация защищенного диска
ADR/Control
| TNO
| | Point
| | | + PLBA + +тек. адр.+
| | | | | | |
LBA - 10EB:00 15 00 0C 01 14 00 02 00 00 34 92 00 00 11 81
LBA - 10EC:00 15 00 0C 01 14 00 AB 00 00 34 B3 00 00 11 85
LBA - 10EF:00 15 00 0C 01 14 00 A0 00 00 22 92 00 00 11 8B
Листинг 48 идентификация защищенного диска
Теперь остается взять CCD-образ, снятый Clone CDCloneCD с оригинального диска, и добавить в него еще одно Entry, несущее в себе трек с нестандартным номером. Легко сказать… Стоит попытаться сделать это как сразу же возникает масса вопросов? Как перевести субканальные данные в формат, понятный коптровщику Clone CDCloneCD? В какое именно место CCD-файла следует добавлять новый Entry? Начнет с ответа на последний вопрос. При просмотре субканальной информации оригинального диска заметно, что трек с нестандартным номером ABh следует за треком номер 02h и предшествует треку A0h (см. листинг 6.56). На приводах с разболтанной головкой эту закономерность установить сложнее, т. к. субканальные данные возращаются в хаотичном порядке и поэтому приходится опираться отнюдь не на те адреса, на которые осуществлялось позиционирование, а на абсолютные адреса, возращенные в самой субканальной информации. Впрочем, об этом мы уже говорили. Так что открываем CCD-образ, находим в нем местечко между треками 02h и A0h… Черт, возьми! Если верить CCD-файлу (а, точнее, информации, возращенной приводом по команде READ TOC, на основании которой и создается CCD-файл), то за Entry с треком 02h следует Entry с треком B0h (стартовый адрес дозаписи), а Entry с треком A0h (номер превого трека текущей сессии) как раз и есть первый Entry данной сессии! И как прикажете это понимать?! А вот как! Стартовый адрес дозаписи в субканальных данных вводных областей вообще не хранится — поэтому-то трека с номером B0h тут и нет.
Далее. Поскольку, TOC каждой сессии дублируется в субканальных данных огромное количество раз, становится понятно, почему за треком ABh следует трек A0h, — ведь TOC по сути свой как бы "закольцован" и вынужден в угоду надежности и отказоустойчивости "пожирать" свой "хвост".
Таким образом, мы должны найти Entry с треком 02h и добавить вслед за ним Entry с треком ABh. Поле Session должно быть равно 2 (ведь мы читали вводную область второй сессии), поле ADR равно четырем старшим битам поля ADR/Control, а после Contorl — соответственно, четырем младшим. В нашем случае ADR/Contorl = 14h, следовательно ADR = 1, а Control = 4. Поле TrackNo = 0, поскольку, мы находится в области Lead-inLead-In (служебном треке с номером ноль). Поля AMin, ASec, AFrame по идее должы содержать адрес соответствующим им субканальных данных, однако, по общему соглашению их принятно считать равными нулю, что соотвествует ALBA адресу в –150 (минус сто пятьдесят). Поле Zero равно нулю по жизни, а поля PMin:PSec:PFrame равны соответствующему им полю PLBA, представленному в формате M:S:F (только помните, что некоторые приводы склонны менять поля PLBA и ALBA местами!). В нашем случае поле PLBA субканальных данных равно 34B3h, что соответствует абсолютному адресу 03:01:66.
Вот, пожалуй, и все. Обобщив весь этот материал, мы получаем следующий симпатичненький Entry, который должен выглядеть приблизительно так как показано в листинге 6.57.
Листинг 6.57. Формирование Entry для копирования защищенного диска
[Entry 12]
Session=2
Point=0xAB
ADR=0x01
Control=0x04
TrackNo=0
AMin=0
ASec=0
AFrame=0
ALBA=-150
Zero=0
PMin=3
PSec=1
PFrame=66
PLBA=13458
Листинг 49 формирование Entry для копирования защищенного диска
Теперь, увеличив содержимое поля TocEntries на единицу и перенумеровав все "хвостовые" Entry так, чтобы только что добавленный нами Entry не нарушал их стройной нумерации, запишем отредактированный образ на новую болванку (добавлять поле [TRACK AB] ни в коем случае не надо!).
Если все сделано правильно, то скопированный диск ничем не будет отличаться от оригинального и защита "упадет". Однако, взломать ее сможет далеко не всякий. Как вы уже могли убедиться, хакер должен обладать весьма глубокими познаниями в области строения лазерных дисков и особенностями их обработки различными моделями приводов.
Умножение в полях Галуа
Открыв учебник математики за третий класс (если мне не изменяет память), мы найдем, что умножение представляет собой многократное сложение и, коль скоро сложение в полях Галуа мы выполнять уже научились, мы имеем все основания считать, что реализация функции умножения не создаст особого труда. Так? А вот и нет! Я всегда знал, что дважды два равно четырем, до конца никогда не верил в это и, впервые столкнувшись с полями Галуа понял, насколько был прав.
Примечание
Другими словами говоря, щелкая выключателем я знаю, что сейчас загорится свет. Но я не уверен в этом (потому что монтер мог перерезатьл провода, могла перегореть лампочка перегорела и т. д.). Вот так и с математикой. Та "жвачка", которой пичкают нас в школе и позже в институте — это не математика. Это набор "шаманских обрядов", который нас заставляют совершать, но который не позволяет приникнуть в самую суть — в дао математики. Может оно и к лучшему, не знаю, но во всяком случае считаю долгом сказать, что "математика" преподаваемая в средних и высших учебных заведениях имеет к математике не больше отношения, чем программирование к терзанию мыши в Word'е и установкеи операционной системы Windows.
Выяснилось, что существуют и такие математики, где дважды два не равно четырем, а операция умножения определяется не через сложение, а совсем по другому.
Действительно, если попытаться "обернуть" функцию gf_sum в цикл, мы получим то же самое сложение только в профиль. a * b будет равно а, если b
четно, и нулю, если b — нечетно. Ну, и кому такое умножение нужно? Собственно, функция "настоящего" умножения Галуа настолько сложна и ресурсоемка, что для упрощения ее реализации приходится к временному преобразованию полиномов в индексную форму, последующему сложению индексов, выполняемому по модулю GF, и обратному преобразованию суммы индексов в полиномиальную форму.
Что такое индекс? Это — показатель степени при основании два, дающий искомый полином.
Например, индекс полинома 8 равен 3 (23 = 8), а индекс полинома 2 равен 1 (21 = 2). Легко показать, что a * b = 2i + 2j = 2(i + j). В частности, 2 * 8 = 23 + 21 = 2(3 + 1) = 44 = 16. Составим следующую к примеру табличку (табл. 2.2) и немного поэкспериментируем с ней.:
Таблица 2.2. Таблица полиномов (левая колонка) и соответствующих
им степеней двойки (правая колонка)
i |
alpha_of[i] |
001 |
0 |
002 |
1 |
004 |
2 |
008 |
3 |
016 |
4 |
Естественно, поскольку от выбранной схемы сопоставления напрямую зависит и конечный результат, обе стороны (кодер и декодер Рида-Соломона) должны соблюдать определенные договоренности. Однако, различные кодеры/декодеры Рида-Соломона могут использовать различные схемы сопоставления, несовместимые друг с другом.
В частности, декодер Рида-Соломона, встроенный в привод CD-ROM привод, выполняет умножение по следующей таблице. Встретив такую таблицу в дизассемблерном листинге исследуемой вами программы, вы сможете быстро и надежно отождествить использующие ее функции (листинг 2.13). Первая слева колонка — полиномы/индексы (обычно обозначается, как i), вторая — таблица степеней примитивного полинома 2 (обычно обозначается как alpha_of), третья — индексы, соответствующие данному полиному (обычно обозначается как index_of):
Листинг 21.1313. Таблица lock-up-таблица для GF(256). Первая слева колонка — полиномы/индексы (обычно обозначается, как i), вторая — таблица степеней примитивного полинома 2 (обычно обозначается как alpha_of), третья — индексы, соответствующие данному полиному (обычно обозначается как index_of)
i alpha index i alpha index ii alpha index i alpha index i alpha index i alpha index |
001 002 0 044 238 240 087 127 189 130 046 192 173 246 252 216 195 251
002 004 1 045 193 18 088 254 241 131 092 247 174 241 190 217 155 96
003 008 25 046 159 130 089 225 210 132 184 140 175 255 97 218 043 134
004 016 2 047 035 69 090 223 19 133 109 128 176 227 242 219 086 177
005 032 50 048 070 29 091 163 92 134 218 99 177 219 86 220 172 187
006 064 26 049 140 181 092 091 131 135 169 13 178 171 211 221 069 204
007 128 198 050 005 194 093 182 56 136 079 103 179 075 171 222 138 62
008 029 3 051 010 125 094 113 70 137 158 74 180 150 20 223 009 90
009 058 223 052 020 106 095 226 64 138 033 222 181 049 42 224 018 203
010 116 51 053 040 39 096 217 30 139 066 237 182 098 93 225 036 89
011 232 238 054 080 249 097 175 66 140 132 49 183 196 158 226 072 95
012 205 27 055 160 185 098 067 182 141 021 197 184 149 132 227 144 176
013 135 104 056 093 201 099 134 163 142 042 254 185 055 60 228 061 156
014 019 199 057 186 154 100 017 195 143 084 24 186 110 57 229 122 169
015 038 75 058 105 9 101 034 72 144 168 227 187 220 83 230 244 160
016 076 4 059 210 120 102 068 126 145 077 165 188 165 71 231 245 81
017 152 100 060 185 77 103 136 110 146 154 153 189 087 109 232 247 11
018 045 224 061 111 228 104 013 107 147 041 119 190 174 65 233 243 245
019 090 14 062 222 114 105 026 58 148 082 38 191 065 162 234 251 22
020 180 52 063 161 166 106 052 40 149 164 184 192 130 31 235 235 235
021 117 141 064 095 6 107 104 84 150 085 180 193 025 45 236 203 122
022 234 239 065 190 191 108 208 250 151 170 124 194 050 67 237 139 117
023 201 129 066 097 139 109 189 133 152 073 17 195 100 216 238 011 44
024 143 28 067 194 98 110 103 186 153 146 68 196 200 183 239 022 215
025 003 193 068 153 102 111 206 61 154 057 146 197 141 123 240 044 79
026 006 105 069 047 221 112 129 202 155 114 217 198 007 164 241 088 174
027 012 248 070 094 48 113 031 94 156 228 35 199 014 118 242 176 213
028 024 200 071 188 253 114 062 155 157 213 32 200 028 196 243 125 233
029 048 8 072 101 226 115 124 159 158 183 137 201 056 23 244 250 230
030 096 76 073 202 152 116 248 10 159 115 46 202 112 73 245 233 231
031 192 113 074 137 37 117 237 21 160 230 55 203 224 236 246 207 173
032 157 5 075 015 179 118 199 121 161 209 63 204 221 127 247 131 232
033 039 138 076 030 16 119 147 43 162 191 209 205 167 12 248 027 116
034 078 101 077 060 145 120 059 78 163 099 91 206 083 111 249 054 214
035 156 47 078 120 34 121 118 212 164 198 149 207 166 246 250 108 244
036 037 225 079 240 136 122 236 229 165 145 188 208 081 108 251 216 234
037 074 36 080 253 54 123 197 172 166 063 207 209 162 161 252 173 168
038 148 15 081 231 208 124 151 115 167 126 205 210 089 59 253 071 80
039 053 33 082 211 148 125 051 243 168 252 144 211 178 82 254 142 88
040 106 53 083 187 206 126 102 167 169 229 135 212 121 41 255 000 175
041 212 147 084 107 143 127 204 87 170 215 151 213 242 157
042 181 142 085 214 150 128 133 7 171 179 178 214 249 85
С помощью данной таблицы вы легко сможете осуществлять преобразование из полиномиальной формы в индексную и наоборот.
Как пользоваться этой таблицей? Допустим, мы хотим умножить полиномы 69 и 96. Находим в первой колонке число 69. Ему соответствует alpha 47, запоминаем (записываем его на бумажке) и переходим к числу 96 alpha которого равен 217. Складываем 47 и 217 по модулю 256, получая в результате: (217 + 47) % 256 = 8. Теперь переводим результат произведения из индексной формы в полиномиальную: находим в первой колонке число 8 и в третьей колонке видим соответствующий ему полином: 3. (Если же мы выполним обратную операцию, разделив 3 на 69 — мы получим 96, что доказывает непротиворечивость операций деления и умножения, а так же всей арифметики Галуа в целиком). Быстро, не правда ли, хотя местами и не совсем понятно, почему таблица составлена именно так, а не иначе? Хуже всего, что достоверность результата нельзя почувствовать "в живую", поскольку все это — абстракции чистейшей воды, что серьезно осложняет отладку программы (сложно отлаживать то, чей принцип работы до конца не понимаешь).
Впрочем, таблицу умножения не обязательно набивать с клавиатуры вручную и ее вполне можно генерировать и "на лету" по ходу исполнения программы. Один из примеров реализации генератора выглядит так как показано в листинге 2.14.:
Листинг 21.14. Процедура генерации таблицы look-up таблицы быстрого умножения полиномов
#define m 8 // степень RS-полинома (согласно Стандарта ECMA-130 [Y66] [n2k67] – восемь)
#define n 255 // n=2**m-1 (длина кодового слова)
#define t 1 // количество ошибок, которые мы хотим скорректировать
#define k 253 // k = n-2*t (длина информационного слова)
// несократимый порождающий полином
// согласно Стандарту ECMA-130: P(x) = x8 + x4 + x3 + x2
+ 1
int p[m+1]={1, 0, 1, 1, 1, 0, 0, 0, 1 };
int alpha_to[n+1]; // таблица степеней примитивного члена
int index_of[n+1]; // индексная таблица для быстрого умножения
//----------------------------------------------------------------------------
// генерируем look- up таблицу для быстрого умножения для GF(2^m) на основе
// несократимого порождающего полинома P(с)© от p[0] до p[m].
//
// look-up таблица:
// index->polynomial из alpha_to[] содержит j=alpha^i,
// где alpha есть примитивный член, обычно равный 2
// а ^ - операция возведения в степень (не XOR!);
//
// polynomial form -> index из
index_of[j=alpha^i] = i;
//
// © Simon Rockliff
//----------------------------------------------------------------------------
generate_gf()
{
int i, mask;
mask = 1; alpha_to[m] = 0;
for (i = 0; i < m; i++)
{
alpha_to[i] = mask;
index_of[alpha_to[i]] = i;
if (p[i] != 0) alpha_to[m] ^= mask;
mask <<= 1;
} index_of[alpha_to[m]] = m; mask >>= 1;
for (i = m+1; i < n; i++)
{
if (alpha_to[i-1] >= mask)
alpha_to[i] = alpha_to[m] ^ ((alpha_to[i-1]^mask)<<1);
else
alpha_to[i] = alpha_to[i-1]<<1;
index_of[alpha_to[i]] = i;
} index_of[0] = -1;
}
Сама же функция умножения выглядит тривиально (листинг 2.15), укладываясь всего в пяток строк. В большинстве программных реализаций кодера/декодера Рида-Соломона, которые мне только доводилось видеть, операция умножения даже не выносится в отдельную процедуру, а реализуется непосредственно по месту вызова.
Листинг 21.15. Функция быстрого табличного умножения полиномов в полях Галуа
// функция возвращает результат умножения
// двух полиномов a на b в полях Галуа
int gf_mul(int a, int b)
{
int sum;
if (a == 0 || b == 0) return 0; // немного оптимизации не повредит
sum = alpha_of[a] + alpha_of[b]; // вычисляем сумму индексов полиномов
if (sum >= GF-1) sum -= GF-1; // приводим сумму к модулю GF
return index_of[sum]; // переводим результат в полиномиальную…
// …форму и возвращаем результат
}
Условные обозначения
Для предотвращения путаницы и одновременно с этим для избежания многословия, в книге вводится ряд условных обозначений, расшифровка которых мы сейчас и займемся.приведенаодится далеениже. В частности условные обозначения CD-приводов::
q
q Под приводом NEC понимается _NEC CD-RW NR-9100A; firmware version 1.4;
q Под приводом ASUS понимается ASUS CD-S500/A; firmware version 1.4;
q Под приводом TEAC понимается TEAC CD-W552E; firmware version 1.09;
q Под приводом PHILIPS понимается PHILIPS CDRW2412A; firmware version 1.5.;
Кроме того представлены следующие обозначения копировщиков:
q Alcohol 120% —–
отличный [Y1] [n2k2] копировщик защищенных дисков, условно-бесплатную версию которого можно получитьутянуть
с сайта http://www.alcohol-soft.com/. Автоматически ломает более половины всех существующих типов защит от копирования и позволяет динамически монтировать образы защищенных дисков на виртуальный привод CD-ROM, что очень удобно для экспериментов. К сожалению, монтированию подлежат лишь "правильные" образы, коими большинство образом защищенных дисков отнюдь не являются.
q
q Clone CD —–
хороший [Y3] [n2k4] копировщик защищенных дисков, условно-бесплатную версию которого можно утянуть скачать по следующему адресу: http://www.elby.ch/. С копированием защищенных дисков в полностью автоматическом режиме Clone CDCloneCD
справляется скорее плохо, чем хорошо, однако, после ручного шаманства с настройками и непосредственно самим образом защищенного диска он может скопировать добрую половину существующих типов защит. Утверждение о том, что Clone CDCloneCD
"берет" практически все существующие защиты от копирования –—
ложное и невероятно далекое от действительности.
Восстановление целых сессий
Альтернативный способ восстановления удаленных файлов заключается в удалении одной или нескольких последних сессий с диска (условимся называть этот метод "очисткой луковицы"). При этом обнажается предыдущий "слой", который теперь становится последним, а, значит, непосредственно доступным для штатных средств операционной системы. Разумеется, физическое удаление сессий с дисков CD-R дисков невозможно в принципе (удаление сессий с дисков CD-RW дисков теоретически вполне возможно, но практически проблема упирается в отсутствие необходимого софта), однако ничто не мешает "скинуть" образ диска в файл, обработать его должным образом и "залить" обратно (на CD-RW –— обратно, а в случае с CD-R нам потребуется свежая болванка). Никто не спорит –— все это нудно и утомительно, но и писать свой "прожигающий" софт (software) ничуть не быстрее.
Хорошо, начинаем готовить экспериментальный диск. Запишем на подопытную болванку одну или несколько сессий, указав программе "прожига" объединять файловую систему новой сессии со всеми предыдущими (обычно так и происходит по умолчанию), а затем добавим еще одну сессию на этот раз стоящую ото всех остальных особняком. Для этого в программе Ahead NeroNero Burning Rom необходимо выбрать пункт "Start Multisession disk" вместо "Continue Multisession", а в Stomp Record Now –— пункт "New Volume"
вместо "Load Last Track". Включаем рекордер (recorder) на "прожиг" и… убеждаемся, что содержимое всех предыдущих сессий оказалось утрачено. Впрочем, справедливости ради следует отметить, что таот же программа Stomp Record Now позволяет легко "починить" загубленный диск –— просто выберите в ниспадающем боксе
[Y20] [n2k21] "Mutlisession" пункт "Load Track 1" и запишите на диск произвольный файл (это необходимо для инициирования процесса записи).
Теперь на диске появится содержимое сессии номер один, а сессия номер два "уйдет в туман". Но это ли беда? Меняем пункт "Load Track 1" на "Load Track 2" и вновь инициируем запись. Теперь на диске появляется содержимое первых двух сессий плюс все те файлы, которые мы были вынуждены записывать, чтобы программа Stomp Record Now согласиласья на "прожиг".
Программа Ahead NeroNero Burning Rom при выборе пункта "Continue Multisession" автоматически запрашивает у пользователя оглавление какой из сессий следует использовать. К сожалению, возможности слияния оглавлений двух и более сессий в текущих версиях не предусмотрено, но в случае возникновения такой необходимости можно прибегнуть к последовательной "каскадной" методике, описанной ранеевыше.
В отсутствие же Stomp Record Now (или аналогичных ей программ) приходится осуществлять восстановление затертых сессий вручную, то бишь при помощи программ наподобие Clone CDCloneCD или Alcohol 120%. Создаем еще один "подопытный" диск, последняя сессия которого перекрывает собой все остальные, "натравливаем" на него Clone CDCloneCD с целью создания образа диска и… теперь остается выкинуть все упоминания о последней сессии из файла IMAGE.CCD. Во-первых, значение поля "Sessions" необходимо уменьшить на единицу, во-вторых, выкинуть все секции [Entry], в которыхчей[n2k22] "Session" [Y23] [n2k24] был последним, в-третьих, вычесть из поля "TocEntries" количество удаленных [Entry], наконец, в-четвертых, удалить последнюю секцию [TRACK]. Теперь отредактированный образ можно смело писать на свежую болванку и… последней сессии как не бывало!
Восстановление данных
Итак, мы знаем какие символы кодового слова искажены, но пока еще не готовы ответить на вопрос: как именно они искажены. Используя полином синдрома и корни полинома локатора, мы можем определить характер разрушений каждого из искаженных символов. Обычно для этой цели используется алгоритм Форни (Forney), состоящий из двух стадий: сначала путем свертки полинома синдрома полиномом локатора L мы получаем некоторый промежуточный полином, условно обозначаемый греческой буквой W (омега). Затем, на основе W-?полинома, вычисляется нулевая позиция ошибки (zero error location), которая в свою очередь делится на производную от L-полинома. В результате получается битовая маска, каждый из установленных битов которой соответствует искаженному биту и для восстановления кодового слова в исходный вид, все искаженные биты должны быть проинвертированы, что осуществляется посредством логической операции XOR (исключающее ИЛИ).
На этом процедура декодирования принятого кодового слова считается законченной. Остается отсечь n – -k символов четности и полученное информационное слово будет готово к употреблению.
Восстановление очищенных CD-RW
Существует две принципиально различных методики очистки CD-RW: быстрая (quick) и полная (full). При быстрой очистке диска с него удаляется лишь область TOC, в результате чего диск выглядит "пустым", хотя его основное содержимое остается совершенно нетронутым. Напротив, при полной очистке луч лазера "выжигает" всю поверхность диска целиком –— от первого пита до последнего. Естественно, на это требуется время и полная очистка диска может растянуться на добрый десяток минут, в то время как быстрая спокойно укладывается в одну-две минуты.
Восстановление полностью очищенных дисков возможно только на специальном оборудовании, способном улавливать даже незначительные изменения отражательной способности рефлекторного слоя. Такое оборудование подавляющему большинству пользователей, разумеется, недоступно. Однако диски, подвергнувшиеся быстрой очистке, могут быть восстановлены и на штатом рекордере (правда, не на всех моделях).
Мы не будем касаться этической стороны проблемы и для простоты предположим, что вы хотите реанимировать свой собственный непредумышленно очищенный диск CD-RW диск, или условимся считать всех читателей сотрудниками КГБ, которым поручили восстановить информацию с диска, добытого бесстрашными советскими разведчиками у американских шпионов. Отметим лишь то, что восстановление конфиденциальной информации с чужих CD-?RW может быть классифицировано как получение несанкционированного доступа к последней со всеми вытекающими отсюда последствиями (на долгие годы –— друзья в клетку и небо – в полоску).
Для опытов по восстановлению информации с очищенных дисков CD-RW дисков нам потребуется следующее:
q пишущий привод не слишком дотошно следящий за корректностью содержимого TOC'a, поддерживающий режим RAW DAO и умеющий читать содержимое области pre-gapPre-gap первого трека. Не все модели пишущих устройствсцов подходят для этой цели, поэтому, будьте готовы к тому, что вам придется перепробовать большое количество различного оборудования (из двух моих рекордеров для восстановления очищенных дисков подходит лишь NEC, а PHILIPS на это, увы, не способен);
q "продвинутый" записывающий софтsoft, позволяющий манипулировать служебными областями диска по своему усмотрению. Вы можете использовать Clone CD, CDRWin, Alcohol 120% или любую другую аналогичную утилиту по своему выбору. Однако, весь последующий материал рассчитан исключительно на Clone CD и при переходе на остальные программы вы можете столкнуться с теми или иными проблемами. Если вы не уверены, что сможете справиться с ними самостоятельно –— используйте Clone CD, ну а затем, по мере приобретения профессиональных навыков и должного опыта, вы без труда восстановите диск любой такой программой;
q средство для работы с диском на сектором уровне, –— утилита, позволяющая прочесть любой заданный сектор (конечно, при условии, что он вообще читается приводом) и не пытающаяся пропустить те сектора, в которых по ее самоуверенному мнению ничего интересного все равно нет. Копировщики защищенных дисков, перечисленные выше, для этой цели не подходят, т. к. отказываются читать "бесполезные" с их точки зрения сектора. Может быть, другие копировщики ведут себя и иначе –— не знаю, не проверял. Вместо этого необходимую для работы утилиту я написал самостоятельно.
Прежде, чем начинать экспериментировать, дайте разберемся, почему после очистки диск перестает читаться. Вопрос не так глуп, каким он кажется, –— ведь информация, необходимая для позиционирования головки и поиска конкретных секторов при быстрой очистке диска остается нетронутой! Управляющие данные "размазаны" вдоль всей спиральной дорожки и для чтения диска на сектором уровне TOC в, общем-то, и не нужен. Да, отсутствие TOC'a значительно усложняет анализ геометрии диска и для определения количества треков/сессий диска, в общем случае, привод должен прочитать весь этот диск целиком. Но при восстановлении информации временной фактор времени играет второстепенную роль и им можно полностью пренебречь.
Тем не менее, при попытке чтения любого из секторов очищенного диска, привод с неизменным упорством возвращает ошибку. Почему? Очень просто, –— это "защита" от чтения заведомо некорректной информации. Еще ни один из всех знакомых мне приводов не мог читать сектора за пределами области Lead-outLead-Out области (собственно, на программном уровне содержимое областей Lead-inLead-In/Lead-outLead-Out областей недоступно тоже). Тем не менее, эта невозможность отнюдь не концептуального уровня и удаление из микропрограммы привода "лишних" проверок позволят прочитать такой диск на ура. Нет, даже не подумайте! Призывать вас к дизассемблированию прошивок я не собираюсь. Дело это сложное, трудоемкое, да к тому же небезопасное. Неверно "хакнутая" прошивка может ко всем чертям угробить привод без малейшей надежды на его восстановление. Нет, уж лучше мы пойдем другим путем!
Идея восстановления информации, предлагаемая автором, в общих чертах сводиться к записи на диск фиктивного TOC, адреса областей Lead-inLead-In и Lead-outLead-Out областей которого указывают на первый и последней сектор диска соответственно, а стартовый адрес первого трека аккурат совпадает с концом области Pre-gapPre-gap области, которая по стандарту должна занимать не менее 150 секторов (или 2 секунд в пересчете на абсолютные адреса). После этой нехитрой операции привод будет читать оригинальное содержимое очищенного диска как миленький, конечно, при том условии, что мы ухитримся настроить пишущий софт так, чтобы он, записав фиктивный TOC, никоим образом не пытался интерпретировать подсунутые ему указатели на области Lead-?In/Lead-?Out области как указание "выжечь" всю поверхность диска целиком.
Проверка показывает, что Clone CD вообще не записывает такой TOC на диск, ругаясь на несоответствие размеров диска и образа файла. Alcohol 120% выполняет нашу просьбу без лишних препирательств, но совсем не так как мы бы хотели! Забив весь восстанавливаемый диск непонятно откуда взятым "мусором", он авторитетно сообщает, что в процессе записи произошли ошибки и, возможно, вам следует убедиться в исправности оборудования.
Хорошо, зайдем с другой стороны. Запишем на диск один реальный трек, занимающий минимально возможное количество секторов (по стандарту –— 300, но некоторые проводы вполне удовлетворяются и меньшими значениями), но расширим его область Pre-gapPre-gap с двух секунд на… весь диск! В результате, мы потеряем лишь 300 последних секторов, но получим доступ ко всему остальному содержимому. Учитывая, что на диске этих секторов насчитывается немногим более 300 тысяч, нетрудно подсчитать, что процент успешно восстановленной информации составляет по меньшей мере 99,999% емкости всего диска, да и то, лишь при том условии, что исходный диск был забит целиком, что в живой природе практически никогда не наблюдается. Если же это вас не удовлетворяет –— разрабатывайте своей собственный софт, корректно записывающий фиктивный TOC, но ничего не делающий сверх этого (область Lead-inLead-In[n2k25] область по любому записывает сам привод, ну а без области Lead-outLead-Out при аккуратном обращении с диском, в принципе, можно и обойтись, главное –— пытаться прочитать сектора, находящиеся за пределами диска, иначе поведение привода станет трудно предсказуемым). Мне же так или иначепо любому это делать лень, –— с восстановлением полностью забитых дисков я еще сталкивался. Во всяком случае пока…
Процедура восстановления состоит из трех частей:
q подготовки исходного образа трека с нормальным значением области Pre-gapPre-gap;
q увеличения области Pre-gapPre-gap до размеров целого диска;
q и записи исправленного образа на восстанавливаемый диск.
Первые два этапа достаточно выполнить всего один раз, т. к. полученный образ (далее мы будем называть его "лечебным") может использоваться для всех дисков (читай: для всех дисков той же самой емкости, по понятным соображениям вы не сможете корректно восстановить 23-минутрый диск с помощью образа, предназначенного для 80-минутного диска и, соответственно, наоборот).
Для начала возьмем чистый диск CD-RW диск ("чистый" не в смысле "ни разу не записанный", а очищенный быстрой или полной очисткой, так же для этих целей подойдет и CD-R). Используя любую утилиту для штатного "прожига", запишем на него один крошечный файл, "весящий" не более 500 Ккилобайт (более "тяжелый" файл просто не уместится в запланированные 300 секторов). Выполнять финализацию диска не нужно.
Запустим Clone CDCloneCD (Alcohol 120%) и снимем образ диска. Спустя минуту-другую на винчестере образуются два файла: file name.img и file name.ccd (если вы попросили Clone CDCloneCD сохранять так же и субканальную информацию, образуется третий файл –— file name.sub, однако, субканальная информация в данном случае будет только мешать, потому опцию "чтение субканалов из треков с данными" лучше всего отключить или же просто удалить file name.sub с диска; так же нам не нужен "Cue-Sheet", который Clone CDCloneCD предлагает создавать для совместимости с другими программами, конкретно –— с CDRWin).
Открыв файл file name.ccd -файл любым текстовым редактором (например, "Блокнотом") найдем в нем следующие строки (листинг 10.6) (ключевые слова для поиска "Point=0xa2" и "Point=0x01").:
Листинг 10.6. Оригинальный стартовый адрес Lead-Out (слева) и стартовый адрес первого трека диска (слева)
[Entry 2] [Entry 3] ; TOC entry
Session=1 Session=1 ; номер сессии
Point=0xa2 Point=0x01 ; point (A2h:leadout/01h:№ трека)
ADR=0x01 ADR=0x01 ; в q-подканале данные позиционир.
Control=0x04 Control=0x04 ; трек с данными
TrackNo=0 TrackNo=0 ; Lead-In трек
AMin=0 AMin=0 ; \
ASec=0 ASec=0 ; +- абсолютный адрес в M:S:F
AFrame=0 AFrame=0 ; /
ALBA=-150 ALBA=-150 ; - абсолютный адрес в LBA [не исп
Zero=0 Zero=0 ; зарезервировано
PMin=0 PMin=0 ; \
PSec=29 PSec=1 ; + - относительный адрес в M:S:F
PFrame=33 PFrame=0 ; /
PLBA=2058 PLBA=0 ; - относительный адрес в LBA
Листинг 6 оригинальный стартовый адрес Lead-Out (слева) и стартовый адрес первого трека диска (слева)
Изменим поля PMin:PSec:PFrame, принадлежащие указателю (point)'у A2h так, чтобы они указывали на самый конец диска (A2h –— это как раз и есть область Lead-outLead-Out и есть). Измененное значение областиый Lead-outLead-Out может выглядеть, например, так: 74:30:00[n2k26] . Адрес Lead-outLead-Out следует выбирать с тем расчетом, чтобы между ним и внешней кромкой диска оставался по меньшей мере 30-секундный зазор. Еще лучше, если ширина области Lead-outLead-Out составит полторы минуты или около того. Однако в этом случае будут неизбежно теряться последние треки восстанавливаемого диска (если, конечно, вам действительно требуется их восстановить).
К содержимому полей PMin:PSec:PFrame, принадлежащих указателюpoint'у 01h (стартовый адрес первого трека) необходимо добавить ту же самую величину, которую вы добавили к соответствующим полям области [n2k27] Lead-outLead-Out'a. Отредактированный вариант может выглядеть, например, так: 74:01:42. (74:30:00 /* новый адрес Lead-outLead-Out */ –– 00:29:33 /* старый Lead-outLead-Out */ + 00:01:00 /* старый стартовый адрес первого трека */ == 74:01:42 /* новый стартовый адрес */).
Короче говоря, новая версия ccd- файла должна выглядеть так как показано в листинге 10.7.:
Листинг 10.7. Ключевой фрагмент "реаниматора" 75-минутных дисков CD-RW
PMin=74 PMin=74
PSec=30 PSec=01
PFrame=00 PFrame=42
Листинг 7 ключевой фрагмент "реаниматора" 75-минутных CD-RW дисков
Вообще-то, для приличия следовало бы скорректировать и поля PLBA (LBA- адрес связан с абсолютным адресом следующим соотношением: LBA == ((Min*60) + Sec)*75 + Frame, однако, текущие версии работают исключительно с абсолютными адресами и LBA-адреса игнорируют. Теперь, все, что находится между концом Lead-inLead-In области и началом первого сектора и будет называться областью Pre-GapPre-gap. При "прожиге" диска область pre-gapPre-gap остается нетронутой и позже может быть прочитана на секторном уровне (а это как раз то, что нам нужно!) Сказать по чести, чрезмерное увеличение pre-gapPre-gap первого трека –— не самая лучшая идея, т. к. не все приводы способны читать такой "жирный" pre-gapPre-gap. С точки зрения совместимости было бы лучше увеличивать pre-gapPre-gap второго трека, однако при этом первый трек придется располагать в самом начале диска и его тело неизбежно затрет восстанавливаемые сектора. И хотя это не такая уж большая проблема (в первых секторах диска все равно ничего ценного нет), к такой мере без особой необходимости все же лучше не прибегать. На крайний случай действуйте так: запишите на диск две сессии и вместо стартового адреса point'a'a 01h меняйте стартовый адрес point'a'a 02h (он будет находится в разделе session=2).
Теперь наскоро очистим наш подопытный диск и до отвала забьем его какими- ни будь файлами (предпочтительнее всего использовать текстовые файлыики –— т. к. в этом случае будет сразу же видно: извлекается ли с восстановленного диска "мусор" или полезная информация).
Записав файлы на диск тут же выполним его быструю очистку.
Убедившись, что диск действительно очищен и его содержимое уже недоступно, запустим Clone CDCloneCD и запишем только что созданным нами "лечебный" образ. Запись должна проводиться в режиме RAW DAO, иначе ничего хорошего у вас не получится (поэтому, прежде чем восстанавливать сколь ни будь ценный диск на еще не известном вам приводе, попробуйте потренироваться на "кошках", –— диске, не содержащем ничего интересного).
Вот наконец мы держим в руках свежевосстановленный диск. Но действительно ли он восстановлен? А вот сейчас и убедимся! Вставляем "воскресшего из пепла" в привод NEC и с замираем сердца пробуем прочитать один из наугад взятых секторов из середины диска (начальные сектора обычно содержат нули, потом –— файловую систему и их очень легко принять за бессмысленный "мусор"). О чудо!!! Оригинальное содержимое очищенного диска читается как ни в чем не бывало!!! Правда, при попытке прочесть оглавление диска средствами операционной системы, привод может впасть в глухую задумчивость, граничащую с полным зависанием (ведь стартовый адрес первого трека расположен не в начале диска, а совсем в другом месте), но это все ерунда! Главное, что на секторном уроне диск все-таки доступен, пускай и не на всех приводах. Так, в частности, дисковод [n2k28] ASUS вообще отказывается читать такой диск, возвращая ошибку, а PHILIPS читает один мусор (к счастью, этот мусор можно восстановить, –— достаточно просто на битовом уровне выполнить EFM-перекодировку с более "правильной" позиции. Поскольку возможных позиций всего 14, перебор обещает не затягиваться на длительное время. Тем не менее, лучше не извращаться, а просто приобрести более качественный привод).
Остается лишь привести диск в состояние, пригодное для "переваривания" операционной системой (что толку в работе с диском на низком уровне?). Последовательно читая все сектора диска один за один, мы будем собирать их в один IMG-файл, для определенности именуемый recover.img.
Сектора, которые не удалось прочитать даже с нескольких попыток, мы будем просто пропускать. Теперь скопируем "лечебный" CCD-файл в recover.ccd и вернем стартовый адрес первого трека на прежнее место. Запишем сформированный образ диска на новую болванку и… (если все сделано правильно) любой привод должен читать ее правильно. Сеанс демонстрационного восстановления окончен и мы, малость освоившись с этой технологией, можем приниматься за вещи куда как более серьезные. Например, откроем собственную компании по восстановлению очищенных дисков. Шутка! Хотя… почему бы и нет?
Хорошо, а как быть если очищенный диск был многосессионным? Ведь описанные выше приемы рассчитаны на работу лишь с одной сессией! На самом деле, можно восстановить и многосессионный диск. Это лишь чуть-чуть труднее. Но, чтобы это сделать, мы должны предварительно познакомиться с остальными полями TOC'а. А это уже тема следующегой разделаглавы!
Постой, а если после очистки диска на него что-то писалось, –— возможно ли тогда его восстановление или нет? Разумеется, непосредственно затертые места утеряны безвозвратно, но остальную часть информации по-прежнему можно спасти. Если диск до очистки был многосессионным, то нам даже не придется корпеть над восстановлением файловой системы, т. к. файловая система каждой последующей сессии обычно дублирует предыдущую ("обычно" это в смысле "за исключением удаленных файлов") и последняя сессия диска оказывается достаточно далеко от его начала, а потому и риск ее затирания –— минимален (если, конечно, схватиться вовремя, а не тогда, когда весь диск перезаписан до отказа). Восстановление одно-сессионных дисков с затертой файловой системой –— намного более трудная, но все-таки разрешимая задача. Во-первых, этих файловых систем на типовом диске целых две: ISO-9660 и Joliet, правда в силу их близкого географического положения при затирании диска они обычно гибнут обе. Во-вторых, указанные файловые системы не поддерживают фрагментации и всякий файл, записанный на лазерный диск, представляет собой единый информационный блок.
Все, что нужно для его восстановления –— определить точку входа и длину. Точка входа в файл всегда совпадает с началом сектора, а подавляющее большинство типов файлов позволяют однозначно идентифицировать свой заголовок по уникальной сигнатуре (в частности, для ZIP-файлов характерна следующая последовательность: 50 4B 03 04). Конец файла, правда, определяется уже не так однозначно и единственная зацепка –— структура самого восстанавливаемого файла. Впрочем, большинство приложений довольно лояльно относится к "мусору" в хвосте файла и потому точностью определения его длины с погрешностью в один сектор на практике оказывается вполне достаточной. Поскольку, файлы располагаются на диске вплотную, без "зазоров", конечный сектор всякого файла надежно вычисляется путем вычитания единицы из стартового сектора следующего за ним файла.
Вообще же говоря, техника восстановления лазерных дисков намного проще и незатейливее искусства врачевания их прямых коллег –— дискет и жестких дисков. Правда, поговорку "семь раз отмерь –— один раз отрежь" еще никто не отменял и одна из пренеприятнейших особенностей работы с CD-RW как раз и состоит в том, что вы не можете гарантированно управлять процессом происходящей записи. Дискеты и жесткие диски в этом смысле полностью прозрачны, –— что вы пишите, то вы и получаете. Перезаписываемые же носители, напротив, представляют собой "черный ящик" и вы никогда не можете быть уверенными в том, что данный конкретный привод будет правильно интерпретировать отдаваемые ему команды (увы, восстановление дисков CD-RW дисков никак не вписывается в рамки Стандарта, а все нестандартные махинации могут интерпретироваться приводом неоднозначно). Единственно, что остается посоветовать –— не пускайте все на самотек, а бесконечно экспериментируйте, экспериментируйте и еще раз экспериментируйте, накапливая бесценный опыт, который вам когда-то очень пригодиться.
Восстановление системы после критического сбоя
"Неестественное, почти половое влечение к кнопке F8 появилось в Кролике совершенно не внезапно"
Щербаков Андрей "14400 бод и 19200 юзеров, и те же самые все-все-все..."
Операционные системы семейства NT достаточно безболезненно переносят критические сбои, даже если те произошли в самый неудобный момент времени (например, в период дефрагментации диска). Отказоустойчивый драйвер файловой системы все сделает сам (хотя запустить файл chkdsk.exe все же не помешает).
Если был выбран "полный дамп памяти" или "дамп памяти ядра", то при следующей успешной загрузке системы жесткий диск будет долго "молотить" головкой, даже если к нему и не происходит никаких обращений. Не пугайтесь! Просто Windows перемещает дамп из виртуальной памяти на место его постоянного "проживания". Запустив "Диспетчер Задач", вы увидите новый процесс в списке –— SaveDump.exe, –— вот он этим и занимается. Необходимость в подобной двухтактной схеме "сброса" дампа объясняется тем, что в момент возникновения критической ошибки работоспособность драйверов файловой системы уже не гарантируется и операционная система не может позволить себе их использовать, ограничиваясь временным размещением дампа в виртуальной памяти. Кстати, если имеющегося объема виртуальной памяти окажется недостаточно (Дополнительно à Параметры быстродействия à Виртуальная память), "сброс" дампа окажется невозможным.
Если же система от загрузки отказывается, упорно забрасывая вас "голубыми экранами смерти", вспомните о существовании клавиши <F8> и выберите пункт "Загрузка последней удачной конфигурации" ("Last Known Good Configuration"). Более радикальной мерой является запуск системы в безопасном (safe) режиме с минимумом загружаемых служб и драйверов. Переустановка системы –— это крайняя мера и без особой нужны к ней лучше не прибегать. Лучше войдите в "консоль восстановления" и переместите файл дампа на другую машину для его дальнейшего исследования.
Восстановление удаленных файлов с CD-R/CD-RW
Заявляя о своей поддержке многосессионных дисков, операционные системы Windows 9x и Windows NT (вплоть до Windows 2000K включительно) тактично умалчивают о том, что поддерживают их лишь частично. Каждая сессия –— это вполне самостоятельный том (в терминологии Windows –— "логический диск"), имеющий свою собственную файловую систему и свои собственные файлы. Благодаря сквозной нумерации секторов лазерного диска, файловая система одной сессии может ссылаться на файлы, физически расположенные в любой другой сессии. Для того, чтобы с многосессионным диском было можно было работать как с единым томом, файловая система последней сессии должна включать в себя содержимое файловых систем всех предыдущих сессий. Если этого не сделать, то при просмотре диска штатными средствами Windows, оглавления остальных сессий окажутся потерянными, поскольку Windows монтирует лишь последнюю сессию диска, а все прочие –— игнорирует. Программы "прожига" CD-?R/CD-?RW по умолчанию добавляют содержимое файловой системы предыдущей сессии к последующей, однако это еще не означает, что последняя сессия диска всегда содержит в себе все то, что имеют предыдущие.
Рассмотрим например, как осуществляется удаление файлов с CD-R/CD-RW. Нет, это не опечатка! Содержимое дисков CD-R, несмотря на физическую невозможность их перезаписи, в принципе все же уничтожаемо. Для имитации удаления файла, программы записи на CD просто не включают ссылку на уничтожаемый файл в файловую систему последней сессии (правда, это умение даровано не всем программам, вот программе Roxio Easy CD Creator'у оно даровано, а, например, Stomp Record Now! –— нет). И хотя "удаленный" файл все еще присутствует на диске, "отъедая" часть дискового пространства, при просмотре содержимого диска из-под Windows он уже не отображается в каталоге. Какой же тогда смысл несет в себе удаление файлов с CD-R, если свободная емкость диска при этом не увеличивается, а даже уменьшается?! –— удивленно спросит иной читатель.
Неверный выбор настроек приводит к утрате содержимого всех предыдущих сессий, но к счастью, эта утрата обратима).
Отсутствие штатных средств выборочного монтирования сессий значительно усложняет жизнь всем пользователям Windows, заставляя искать обходные пути. В идеале, конечно, следовало бы реализовать своей собственный драйвер CDFS[n2k17] драйвер, обладающий максимумом функциональных возможностей. Однако, это довольно сложная и трудоемкая задача, оправдывающая себя лишь в тех случаях, когда с необходимостью восстановления удаленных файлов вы сталкиваетесь по несколько раз на дню. Гораздо проще написать набор утилит для непосредственной работы с диском на физическом уровне. Собственно, все, что нам требуется –— это отобразить содержимое файловой системы в удобно-читаемом виде. Конкретно нас интересует: имя файла, его стартовый адрес и длина. Знания трех этих важнейших атрибутов без труда позволяют "сграбить" (grab) файл на жесткий диск, где вы сможете работать с ним в свое удовольствие. Такая методика идеальна для восстановления небольших количеств удаленных (перезаписанных) файлов из произвольных сессий, но она непрактична в тех ситуациях, когда приходится восстанавливать всю сессию целиком и тогда обычно прибегают к копированию восстанавливаемых сессий на отдельный CD-R/CD-RW диск.
Встроенная защита CD-дисков
Защита от несанкционированного копирования была заложена в лазерные диски изначально. Даже в те незапамятные времена, когда еще не существовало ни персональных компьютеров, ни лазерных дисков с данными, ни самой проблемы пиратства, "Красная книга" уже описывала специальный бит digital copy prohibited/permitted, запрещающий цифровое чтение с носителя, если держателю авторских прав было так угодно (см. поле CONTROL Q-канала подкода, если первый, считая от нуля, бит сброшен, то цифровое чтение разрешено и, соответственно, наоборот). Однако, ни один их известных автору копировщиков этот бит не учитывает (Ahead Nero Burning ROM выдает предостерегающее предупреждение, но не более того), и ни один из приводов CD-ROM приводов не блокирует цифровое чтение, даже если оно и "запрещено". Отчасти это вызвано тем, что цифровое воспроизведение имеет значительные преимущества перед аналоговым (меньше посторонних шумов, программная коррекция звука и т. д.), чем и объясняется стремительное вытеснение последнего с рынка. Устройству, не поддерживающему цифровое воспроизведение, будет очень трудно найти своего покупателя, вот производителям и приходиться игнорировать стандарт в угоду интересам пользователя.
Актуальность защиты лазерных дисков сегодня
Актуальность защиты лазерных дисков сегодня как никогда велика. Широкое внедрение распространение бытовых рекордеров позволило пользователем тиражировать диски чуть ли не в промышленных масштабах, ну илиили, по крайней меремере,
львиную долю дисков не покупать, а взять взайимыствоватьодалживать у приятелей. В то же время многие shareware-программисты распространяют свои продукты на CD-R дисках по почте, что значительно усложняет задачу хакеров (т.к. если программы нет в открытом доступе в открытом доступе, то как ее прикажете ломать?).
В итоге, пользователи интересуются как ломать защищенные диски, а разработчики –— как защитить эти диски так, чтобы их не взломали. Данная книгаКнига "техника защиты лазерных дисков от копирования" удовлетворяет интересы обоих групп. Она рассказывает о том, как взламываются практически все, существующие на сегодняшний день защитные пакеты, и предлагает ряд новых, принципиально не взламываемых защитных механизмов.
Книга "техника защиты лазерных дисков от копирования" содержит большое количество уникального, ранее нигде не публиковавшегося материала. Она дает читателю исчерпывающее представление о структуре CD и раскрывает множество секретов, известныхм только профессионалам высочайшего класса (да и то
не всем), причем материал изложен ухитряется все это изложить в доступной форме без вышей математике и практически без ассемблера.
Прочитав эту книгу, читатель,
(даже если он и не имеял
специальной подготовки,) научится создавать действительно принципиально некопируемые диски и эта принципиальность будет гарантироваться аппаратными ограничениями современных CD-?R/CD-?RW рекордеров. Помимо того, читатель узнает как избежать конфликтов с нестандартным оборудованием, из-за которых защита отказывается работать у некоторых пользователей или, что еще хуже, приводит к порче их оборудования.
Книга ориентирована на широкой спектр читательской аудитории. По минимуму –— никакой специальной подготовки от читателя и не требуется, он даже может не знать из каких секторов состоит CD-ROM (99% программистов этого, кстати, и не знают). Вся информация, необходимая для осмысленной работы с CD-ROM'ом, изложена непосредственно в самой книге и отсылки к посторонним источникам минимальны. Читатель не обязательно должен уметь программировать, т. к. все необходимые утилиты для анализа/защиты/взлома лазерных дисков уже прилагаются к книге. Наконец, читатель может воспользоватьсядаже не уметь читать, –
автоматическимие копировщикамии, разработаннымие
автором, которые все сделают за него. Так что книгу стоит покупать уже ради одного содержимого прилагаемого к ней CD.
По максимуму –— читатель должен знать математику в объеме вузовской программы, "уметь держать в руках"
дизассемблер и "свободно говорить" на Си и ассемблере. Чтение настоящей книги, конечно, не сделает его "богом", но: безграничную власть над лазерными дисками он все-таки получит и сможет вытворять с ними то, что другим и не снилась.
Взаимодействие посредствомчерез портовы ввода/вывода
Операционная система Windows NT тщательно оберегает порты ввода/вывода от посягательства со стороны прикладных приложений. Мера эта вынужденная и реализованная под давлением выбранной политики безопасности. Свобода прикладных приложений умышленно ограничивается так, чтобы предотвратить возможные "террористические акты", направленные на подрыв системы или несанкционированный захват конфиденциальной информации. Правом непосредственного доступа к оборудованию обладают лишь драйверадрайвераы и динамические библиотеки, исполняющиеся в режиме ядра (см. разд. "Доступ посредствомчерез SCSI мини- порта интерфейса SCSI" этой главы).
Поневоле вспоминаются слова одного из "отцов-основателей" США, что нация, обменявшая свободу на безопасность, не заслуживает ни того, ни другого. ИИ, правда! Как будто бы нельзя "завесить" систему посредствомчерез техот же тнтерфейсов SPTI или //ASPI! Причем для этого даже не понадобится обладать правами администратора! Какая там политика безопасности, какое к черту разграничение доступа, когда интерфейс ASPI дает доступ к диску на секторном уровне безо всяких проверок на предмет правомерности осуществления этой операции. Хоть сейчас внедряй boot-вирусы в загрузочный сектор внедряй! И это при том, что отсутствие доступа к портам ввода/вывода существенно усложняет задачу управления оборудованием и уж тем более создания надежных и трудноломаемых защитных механизмов!
Операционные системы семейства Windows 9x ведут себя более "демократично", однако их "снисходительность" распространяется исключительно на программы операционной системы MS-DOS программы, а win32-приложения возможности прямого доступа к портам, увы, лишены.
Тем не менее, управлять оборудованием с прикладного уровня все-таки возможно. Существует по меньшей мере два пути решения этой проблемы:
q а) создание драйвера-посредника, реализующего более или менее прозрачный интерфейс для взаимодействия с портами через механизм IOCTL; и
q б) модификация карты разрешения ввода-вывода (I/O Permission Map, –— IOPM) с таким расчетом, чтобы обращение к портам перешло в разряд непривилегированных операций, осуществимых и с прикладного уровня.
ДалееНиже оба этих способа будут подробнои рассмотрены. Начнем с интерфейсного драйвера.
В состав NT DDK входит весьма любопытный учебный драйвер PORTIO, создающий виртуальное устройство и реализующий специальный IOCTL-интерфейс, посредством которого прикладные приложения могут манипулировать с портами этого устройства произвольным образом (его исходный текст, с минимумом необходимых комментариев расположен в каталоге: "\NTDDK\src\general\portio)"). Конечно, виртуальное устройство, –— это не совсем то, что нам нужно, поскольку диапазон принадлежащих ему портов ввода/вывода не может пересекаться с портами, принадлежащими другими устройствам, в противном случае система "грязно выругается" и поставит в "диспетчере устройств" восклицательный знак, предупреждая пользователя об имеющемся конфликте ресурсов. И хотя на работоспособность системы такой конфликт никак не повлияет, созерцание восклицательных знаков уж точно не пойдет в прокна пользу пользователям нашей программы.
На самом деле, драйверу, работающему в режиме ядра, никто не запрещает обращаться к любым портам, каким ему только вздумается. Достаточно исключить из тела [Y133] файла genport.c следующие строки (листинг 1.4.24), и мы сможем с его помощью читать весь диапазон портов ввода/вывода.
Листинг 2.1.4.2524. Проверка адресов портов, к которым происходит обращение на принадлежность к диапазону портов виртуального устройства, созданного драйверов. Для того, чтобы иметь возможность обращаться к любым портам, эти строки следует удалить
if (nPort >= pLDI->PortCount ||
(nPort + DataBufferSize) > pLDI->PortCount ||
(((ULONG_PTR)pLDI->PortBase + nPort) & (DataBufferSize - 1)) != 0)
{
return STATUS_ACCESS_VIOLATION; // Illegal port number
}
Также следует обратить внимание на то, что драйвер ожидает получить не абсолютный адрес порта, а относительный, отсчитываемый от адреса базового порта, задаваемого при добавлении виртуального устройства в систему (листинг 1.4.25). Взгляните на следующие строки:
Листинг 2.1.4.2625. Вычисление действительного адреса порта через базовый
case IOCTL_GPD_READ_PORT_UCHAR:
*(PUCHAR)pIOBuffer=READ_PORT_UCHAR((PUCHAR)((ULONG_PTR)pLDI->PortBase+nPort));
break;
Очевидно, что текст, выделенный жирным шрифтом следует удалить, –— в этом случае драйвер сможет оперировать абсолютными, а не относительными портами, и мы без труда сможем "прорваться" к любому порту системы! Причем, если мы перенесем модифицированный нами драйвер на Windows 9x, наши приложения будут работать в обеих операционных системах и останутся зависимыми разве что от самого оборудования. Но, с другой стороны, всякий, кто стремится дорватьсядобраться до портов, должен отдавать себе отчет в том, зачем это ему нужно и какие сложности ему придется преодолеть.
Конечно, поскольку возможность бесконтрольного доступа ко всем имеющимся портам ввода/вывода существенно ослабляет и без того уязвимую операционную систему, нелишним будет ввести в драйвер кое-какие дополнительные проверки и ограничения. Скажем, запретить прямое обращение ко всему, что не является приводом CD-ROM приводом. В противном случае, если ваша программа получит сколь- ни будь широкое распространение, толпы вандалов ринуться писать зловредных троянских коней, военная мощь которых окажется практически безграничной, и совладеть с ними будет очень и очень трудно. С другой стороны, –— за все время существования интерфейса ASPI не было зафиксировано ни одной попытки использовать его для деструктивных целей, хотя такая возможность до сих пор имеется.
Другой недостаток предложенного способа управления устройствами заключается в его катастрофически низком быстродействии.
Вызовы функции DeviceIoControl распадаются на десятки тысяч машинных команд (!), "благодаря" чему время обработки запросов становится слишком большим, а измерение физических характеристик спиральной дорожки (если мы действительно захотим эти характеристики измерять) –— неточным. К тому же, функция DeviceIoControl громоздка и неизящна, а самое неприятное в том, что на нее очень легко поставить BreakPoint[Y134] [n2k135] , и потому участь такой защиты заранее предрешена. Во времена операционной системы MS-DOS, когда взаимодействие с оборудованием осуществлялось посредством машинных команд IN и OUT, локализовать защитный код в теле программы было значительно сложнее, а управлять устройствами с их помощью существенно легче и –— главное –— намного производительнее.
Считается, что в среде Windows NT прямое обращение к портам возможно только на уровне ядра, а приложения вынуждены общаться с портами через высокоуровневый интерфейс, предоставляемый драйвером. И хотя этот интерфейс может быть полностью прозрачным (драйверу ничего не стоит перехватить исключение, возникающее при попытке чтения/записи в порт с прикладного уровня, и выполнить этот запрос самостоятельно), и это все-таки это не то…
На самом деле, выполнять команды IN/OUT можно и на прикладном уровне, правда не без помощи недокументированных возможностей операционной системы и документированных, но малоизвестных особенностей реализации защищенного режима работы в процессорах Intel 80386+. Вот с процессоров мы, пожалуй, и начнем. Давайте откроем [Y136] [n2k137] "Instruction Set Reference" и посмотрим, как "устроена" машинная команда OUT. Среди прочей полезной информации мы найдем и ее псевдокод, которой выглядит приблизительно так, как это показано в листинге 1.4.26.:
Листинг 2.1.4.2726. Псевдокод инструкции OUT
if ((PE == 1) && ((CPL > IOPL) || (VM == 1)))
{
/* Protected mode with CPL > IOPL or virtual-8086 mode */
if (Any I/O Permission Bit for I/O port being accessed == 1)
#GP(0); /* I/ O operation is not allowed */
else
DEST ß SRC; /* Writes to selected I/O port */
}
else
{
/* Real Mode or Protected Mode with CPL <= IOPL */
DEST ß SRC; /* Writes to selected I/O port */
}
Обратите внимание! Обнаружив, что полномочий текущего уровня привилегий категорически недостаточно для выполнения данной машинной инструкции, процессор не спешит выбросыватьитьвыдать исключение general protection fault, а предоставляетдает ей еще один шанс, осуществляя дополнительную проверку на предмет состояния карты разрешения ввода/вывода (I/O permission bitmap) и, если бит памяти, соответствующий данному порту не равен единице, то вывод в порт осуществляется несмотря ни на какие запреты со стороны CPL[Y138] [n2k139] !
Таким образом, для взаимодействия с портами с прикладного уровня нам достаточно всего лишь скорректировать карту разрешения ввода/вывода, после чего подсистема защиты операционной системы Windows NT перестанет нам мешать, поскольку контроль доступа к портам осуществляется не на программном, а на аппаратном уровне и, если процессор перестанет выбрасыватьвыдавать исключения, то операционная система ничего не узнает о происходящем!
Проблема в том, что подавляющее большинство авторов книг по ассемблеру о карте разрешения ввода/вывода даже не упоминают, и лишь немногие программисты знают о ее существовании –— те, кто предпочитает оригинальную документацию корявым переводам и пересказам.
Обратившись к [Y140] [n2k141] "Architecture Software Developer's Manual Volume 1: Basic Architecture", мы узнаем, что карта ввода/вывода находится в сегменте состояния задачи (TSS –— Task State Segment), –— точнее, ее действительное смещение относительно начала TSS определяется 32-битным полем, расположенном в 0x66 (102) и 0x67 (103) байтах сегмента состояния задачи.
Нулевой бит этой карты отвечает за нулевой порт, первый –— за первый, второй –— за второй и т. д. вплоть до старшего бита 0x2000 (8192) -байта, отвечающего за 65 535 порт. Битовую карту завершает так называемый байт-терминатор, имеющий значение 0xFF. Вот, собственно, и все. Порты, чьи биты сброшены в нулевое значение, доступны с прикладного уровня безо всяких ограничений. Разумеется, сама карта ввода/вывода доступна лишь драйверам, но не приложениям, поэтому без написания собственного драйвера нам все равно не обойтись. Однако этот драйвер будет работать только на стадии своей инициализации, а весь дальнейший ввод/вывод пойдет напрямую, даже если выгрузить драйвер из памяти.
Теперь плохая новость. В Windows NT смещение карты ввода/вывода по умолчанию находится за пределами сегмента состояния задачи и потому модифицировать карту ввода/вывода не так-то просто, поскольку ее вообще нет! Процессор, кстати говоря, на такую ситуацию реагирует вполне спокойно, но доступ к портам ввода/вывода с прикладного уровня тем не менее запрещает.
На самом же деле карта ввода/вывода в сегменте TSS все-таки есть, но она умышленно заблокирована системой, чтобы не дать прикладным приложениям своевольничать. Исключение составляют лишь высокопроизводительные графические библиотеки, напрямую обращающиеся к портам ввода/вывода с прикладного режима. Как нетрудно догадаться, такой трюк дает Microsoft значительную фору перед конкурентами, вынужденными управлять портами либо с уровня ядра, либо посредствомчерез интерфейса, предоставляемогоый видеодрайвером. Естественно, оба этих способа значительно проигрывают в производительности прямому доступу к портам.
Однако попытка подкорректировать указатель на карту ввода/вывода ни к чему не приводит, поскольку коварная операционная система Windows NT хранит копию этого значения в контексте процесса, а потому при переключении контекста указатель на прежнюю карту автоматически восстанавливается. С одной стороны это хорошо, так как каждый процесс может иметь свою собственную карту ввода/вывода, а с другой… штатная документация от Microsoft не содержит и намека на то, как с этой картой работать.
Правда, можно схитрить и увеличить размер сегмента состояния задачи так, чтобы адрес карты ввода/вывода, прежде указывающий за его конец, теперь приходился на действительную и подвластную нам область памяти. А поскольку в хвосте последней страницы, занятой сегментом TSS, имеется всего лишь 0xF55 (3925) байт, максимальный размер карты, которую мы только можем создать в этом промежутке, охватывает всего лишь 31 .392 портов ввода/вывода. Хотя, если говорить честно, остальные порты нам все равно вряд ли понадобятся, так что ничего трагичного в таком ограничении и нет.
Впрочем, существуют и более изящные способы решения этой проблемы. Усилиями Дейла Робертса были обнаружены три полностью недокументированные функции: Ke386SetIoAccessMap(), Ke386QueryIoAccessMap() и Ke386IoSetAccessProcess(), которые, как и следует из их названий, обеспечивают вполне легальный способ управления картой ввода/вывода. "Полностью недокументированные" в том смысле, что даже заголовочные файлы из DDK не содержат их прототипов (а, как известно, в заголовочных файлах DDK перечислено множество недокументированных функций). Тем не менее, библиотека NTOSKRNL их все-таки экспортирует, и они легко доступы с уровня драйверов.
Подробнее обо всем этом можно прочитать в статье их первооткрывателя –— Дейла Робертса, –— перевод которой можно найти, в частности, по следующему адресу: http://void.ru/?do=printable&id=701. Здесь же мы рассмотрим их лишь кратко. Итак, функция Ke386SetIoAccessMap принимает два аргумента: двойное слово, которое будучи установленным в единицу, заставляет функцию копировать карту ввода/вывода указатель, на которую передан ей со вторым аргументом. Функция Ke386QueryIoAccessMap принимает те же самые аргументы, но осуществляет прямо противоположную операцию, извлекая текущую карту ввода/вывода из сегмента состояния задачи и копируя ее в указанный буфер. Наконец, функция Ke386IoSetAccessProcess принимает со своим вторым аргументом указатель на структуру процесса, полученный вызовом документированной функции PsGetCurrentProcess().
Первый аргумент играет ту же самую роль, что и в предыдущих функциях: нулевое значение переводит указатель на карту ввода/вывода за границы сегмента TSS, тем самым запрещая доступ к портам с прикладного уровня, а единичное –— активизирует ранее переданную карту ввода/вывода.
Пример драйвера, открывающего прямой доступ к портам ввода/вывода на прикладном уровне и приведенный в листинге 1.4.27ниже, все это, собственно, и демонстрирует.:
Листинг 2.1.4.2827. [/etc/GIVEIO.c] Демонстрационный пример драйвера, открывающего прямой доступ к портам ввода/вывода на прикладном уровне
/*----------------------------------------------------------------------------
*
* ДРАЙВЕР. РАЗРЕШАЕТ ВЫПОЛНЕНИЕ
* МАШИННЫХ КОМАНД IN/OUT НА ПРИКЛАДНОМ УРОВНЕ
* ===========================================
*
* ВНИМАНИЕ! Я, Крис Касперски, не имею никакого отношения к этой программе!
* -------------------------------------------------------------------------
*
* GIVEIO.SYS: by Dale Roberts
* КОМПИЛЯЦИЯ: Используйте средство DDK BUILD
* НАЗНАЧЕНИЕ: Предоставить доступ к прямому в/в процессам режима пользователя
----------------------------------------------------------------------------*/
#include <ntddk.h>
/* Имя нашего драйвера устройства */
#define DEVICE_NAME_STRING L"giveio"
// Структура" IOPM. это просто массив байт размером 0x2000, содержащий
// 8К * 8 бит == 64К бит IOPM, которые покрывают всё 64 Кб адресное
// пространство ввода/вывода x86 процессоров.
// Каждый нулевой бит предоставляет доступ к соответствующему порту
// для user-mode процесса; каждый единичный бит запрещает доступ к в/в
// через соответствующий порт
#define IOPM_SIZE 0x2000
typedef UCHAR IOPM[IOPM_SIZE];
// массив нулей, который копируется в настоящую IOPM в TSS посредством
// вызова dsKe386SetIoAccessMap()
// необходима память выделяется во время загрузки драйвера
IOPM *IOPM_local = 0;
// это две полностью недокументированные функции, которые мы используем,
// чтобы дать доступ к в/в вызывающему процессу
// * Ke386IoSetAccessMap() - копирует переданную карту в/в в TSS
// * Ke386IoSetAccessProcess() - изменяет указатель смещения IOPM, после
// чего только что скопированная карта
// начинает использоваться
void Ke386SetIoAccessMap(int, IOPM *);
void Ke386QueryIoAccessMap(int, IOPM *);
void Ke386IoSetAccessProcess(PEPROCESS, int);
// ОСВОБОДИТЬ ВСЕ ВЫДЕЛЕННЫЕ РАНЕЕ ОБЪЕКТЫ
VOID GiveioUnload(IN PDRIVER_OBJECT DriverObject)
{
UNICODE_STRING uniDOSString;
WCHAR DOSNameBuffer[] = L"\\DosDevices\\" DEVICE_NAME_STRING;
if(IOPM_local) MmFreeNonCachedMemory(IOPM_local, sizeof(IOPM));
RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);
IoDeleteSymbolicLink (&uniDOSString);
IoDeleteDevice(DriverObject->DeviceObject);
}
//----------------------------------------------------------------------------
// устанавливаем IOPM (карту разрешения в/в) вызывающего процесса так, чтобы
// ему предоставлялся полный доступ к в/в. Массив IOPM_local[] содержит
// одни нули, соответственно, IOPM обнулится.
// Если OnFlag == 1, процессу предоставляется доступ к в/в;
// Если он равен 0, доступ запрещается.
//----------------------------------------------------------------------------
VOID SetIOPermissionMap(int OnFlag)
{
Ke386IoSetAccessProcess(PsGetCurrentProcess(), OnFlag);
Ke386SetIoAccessMap(1, IOPM_local);
}
void GiveIO(void)
{
SetIOPermissionMap(1);
}
//----------------------------------------------------------------------------
// cлужебный обработчик для user-mode вызова CreateProcess().
// эта функция введена в таблицу вызовов функций объекта драйвера с помощью
// DriverEntry().
когда user-mode приложение вызывает CreateFile(), эта
// функция получает управление всё ещё в контексте вызвавшего приложения,
// но с CPL (текущий уровень привилегий процессора) установленым в 0.
// Это позволяет производить операции возможные только в kernel mode.
// GiveIO вызывается для предоставления вызывающему процессу доступа к в/в.
// Все, что приложение режима пользователя, которому нужен доступ к в/в,
// должно сделать - это открыть данное устройство, используя CreateFile()
// Никаких других действий не нужно.
//----------------------------------------------------------------------------
NTSTATUS GiveioCreateDispatch(IN PDEVICE_OBJECT DeviceObject,IN PIRP Irp)
{
GiveIO(); // give the calling process I/O access
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_SUCCESS;
IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS;
}
//----------------------------------------------------------------------------
// процедура входа драйвера. эта процедура вызывается только раз после
// загрузки драйвера в память. она выделяет необходимые ресурсы для работы
// драйвера. в нашем случае она выделяет память для массива IOPM и создаёт
// устройство, которое может открыть приложение режима пользователя.
// она также создаёт символическую ссылку на драйвер устройства.
// что позволяет user-mode приложению получить доступ к нашему драйверу
// используя \\.\giveio нотацию.
//----------------------------------------------------------------------------
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath)
{
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
UNICODE_STRING uniNameString, uniDOSString;
WCHAR NameBuffer[] = L"\\Device\\" DEVICE_NAME_STRING;
WCHAR DOSNameBuffer[] = L"\\DosDevices\\" DEVICE_NAME_STRING;
// выделим буфер для локальной IOPM и обнулим его
IOPM_local = MmAllocateNonCachedMemory(sizeof(IOPM));
if(IOPM_local == 0) return STATUS_INSUFFICIENT_RESOURCES;
RtlZeroMemory(IOPM_local, sizeof(IOPM));
// инициализируем драйвер устройства и объект устройства (device object)
RtlInitUnicodeString(&uniNameString, NameBuffer);
RtlInitUnicodeString(&uniDOSString, DOSNameBuffer);
status = IoCreateDevice(DriverObject, 0, &uniNameString,FILE_DEVICE_UNKNOWN,
0, FALSE, &deviceObject);
if(!NT_SUCCESS(status)) return status;
status = IoCreateSymbolicLink (&uniDOSString, &uniNameString);
if (!NT_SUCCESS(status)) return status;
// инициализируем точки входа драйвера в объекте драйвера
// всё, что нам нужно, это операции создания (Create) и выгрузки (Unload)
DriverObject->MajorFunction[IRP_MJ_CREATE] = GiveioCreateDispatch;
DriverObject->DriverUnload = GiveioUnload;
return STATUS_SUCCESS;
}
Пример демонстрации ввода/вывода в порт с прикладного уровня показан в листинге 1.4.28.
Листинг 2.1.4.2928. [/etc/GIVEIO.demo.c] Пример ввода/вывода в порт с прикладного уровня
/*----------------------------------------------------------------------------
*
* ДЕМОНСТРАЦИЯ ВЫЗОВА IN/OUT НА ПРИКЛАДНОМ УРОВНЕ
* (внимание! драйвер GIVEIO.SYS должен быть предварительно загружен!)
* ====================================================================
*
* ВНИМАНИЕ! Я, Крис Касперски, не имею никакого отношения к этой программе!
* -------------------------------------------------------------------------
*
* GIVEIO.TST: by Dale Roberts
* НАЗНАЧЕНИЕ: Тестирование драйвера GIVEIO, производя какой-нибудь в/в.
* : (мы обращаемся к внутреннему динамику PC)
----------------------------------------------------------------------------*/
#include <stdio.h>
#include <windows.h>
#include <math.h>
#include <conio.h>
typedef struct {
short int pitch;
short int duration;
} NOTE;
// ТАБЛИЦА НОТ
NOTE notes[] = {{14, 500}, {16, 500}, {12, 500}, {0, 500}, {7, 1000}};
// УСТАНОВКА ЧАСТОТЫ ДИНАМИКА PC В ГЕРЦАХ
// ДИНАМИК УПРАВЛЯЕТСЯ ТАЙМЕРОМ INTEL 8253/8254 С ПОРТАМИ В/В 0X40-0X43
void setfreq(int hz)
{
hz = 1193180 / hz; // базовая частота таймера 1.19MHz
_outp(0x43, 0xb6); // Выбор таймера 2, операция записи,режим 3
_outp(0x42, hz); // устанавливаем делитель частоты
_outp(0x42, hz >> 8); // старший байт делителя
}
//-----------------------------------------------------------------------------
// длительность ноты задается в долях частоты 400 Hz, число 12 задает масштаб
// Cпикер управляется через порт 0x61. Установка двух младших битов разрешает
// канал 2 таймера 8253/8254 и включает динамик.
//-----------------------------------------------------------------------------
void playnote(NOTE note)
{
_outp(0x61, _inp(0x61) | 0x03); // включаем динамик
setfreq((int)(400 * pow(2, note.pitch / 12.0))); Sleep(note.duration);
_outp(0x61, _inp(0x61) & ~0x03); // выключаем
}
//----------------------------------------------------------------------------
// открытие и закрытие устройства GIVEIO, что дает нам прямой доступ к в/в;
// потом пытаемся проиграть музыку
//----------------------------------------------------------------------------
int main()
{
int i;
HANDLE h;
h = CreateFile("\\\\.\\giveio", GENERIC_READ, 0, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL);
if(h == INVALID_HANDLE_VALUE)
{
printf("Couldn't access giveio device\n"); return -1;
}
CloseHandle(h);
for(i = 0; i < sizeof(notes)/sizeof(int); ++i) playnote(notes[i]);
return 0;
}
Теперь поговорим о том, как данный способ взаимодействия с портами ввода/вывода может быть использован на благо защитных механизмов. Допустим, наша защита привязывается к физическому дефекту поверхности лазерного диска. Тогда все, что нам надо –— попытаться как можно незаметнее прочитать этот сектор: если он действительно не читается, диск можно считать оригинальным и наоборот. Прямое управление приводом через порты ввода/вывода с вероятностью близкой к единице останется незамеченным даже бывалыми хакерами, потому такой вариант им попросту не придет в голову! Единственное, о чем следует позаботиться, –— не дать обнаружить защитный код по перекрестным ссылкам, оставленных "ругательным" сообщением, которое выводится на экран в том случае, если диск признан пиратским.
Тем не менее, матерых хакеров на такую наживку не возьмешь! Злорадно ухмыльнувшись, они просто поставят точку останова на ввод/вывод в порты 0x1F7/0x177 (для Primary и Secondary приводов соответственно). А чтобы не утонуть в море обращений к приводу посредствомчерез [n2k142] функции API, задействуют условные точки останова, приказывая отладчику "всплывать" только в том случае, если адрес машинной команды, осуществляющей ввод/вывод, находится ниже адреса 0x70000000, т. е., другими словами, принадлежит пользовательскому приложению, а не ядру.
Но что нам мешает с прикладного уровня выполнить команду ввода/вывода по адресу, принадлежащему ядру? Достаточно просто просканировать верхнюю половину адресного пространствао на предмет наличия команд OUT DX, AL (опкод 0xEE) и IN AL, DX (опкод 0xEC). Спрашиваете: а как мы сможем вернуть управление? Да очень просто, –— с помощью обработки структурных исключений. Если машинная команда, следующая за IN/OUT, возбуждает исключение (а таких команд –— хоть пруд пруди), то, перехватив его, мы сможем как ни в чем не бывало продолжить выполнение программы как ни в чем не бывало.
Достоинство этого приема в том, что точка останова, поставленная хакером на порты ввода/вывода, не сработает (точнее, сработает, но будет тут же проглочена фильтром), а недостаток: неоправданное усложнение защитного механизма.
Взаимодействие посредствомчерез собственный драйвер
Несмотря на то, что Windows позволяет управлять устройствами и с прикладного уровня, достаточно многие разработчики предпочитают осуществлять такое управление через свой собственный драйвер, который может взаимодействовать с приводом как напрямую, так и через его драйвер. Последний способ более предпочтителен, поскольку он позволяет абстрагироваться от конкретного оборудования и обеспечивает единый унифицированный интерфейс для всех приводов. Большинство таких драйверов "подключаются" к ATAPI и/или SCSI-порту и взаимодействуют с диском приблизительно так же, как и ASPI-драйвер, уже рассмотренный нами.
Взаимодействие с прикладными приложениями обычно осуществляется посредством специальных кодов IOCTL, передаваемых драйверу функцией DeviceIoControl. "Специальных", –— потому что разработка протокола взаимодействия драйвера с устройством целиком лежит на совести (и фантазии) создателя этого самого драйвера, и никакой стандартизацией здесь даже отдаленно не пахнет! К тому же, применение функции DeviceIoControl –— это не единственно возможный вариант. Драйверу, исполняющемуся в нулевом кольце, формально доступны все ресурсы операционной системы, и при желании можно осуществить самые "крутые извращения". Например, взаимодействовать с приложением через общую область памяти. Тогда точки останова, установленные на функции DeviceIoControl не дадут никакого результата! Однако подавляющее большинство драйверов работают посредствомчерез IOCTL и не блистают оригинальностью. В каком-то смысле такая позиция вполне оправдана. Действительно, с ростом извращенности драйвера увеличивается и его конфликтность, а совместимость с другими программами (и операционными системами) резко падает. К тому же, "навороченный" драйвер значительно труднее довести до ума, чем простой. С другой стороны, неизвращенный драйвер очень легко взломать, и его разработка ничем не оправдает себя. Уж лучше воспользоваться тем же ASPI, который обеспечивает полнофункциональный низкоуровневый и при этом системно-независимый интерфейс. Тогда вам не придется создавать реализации своего драйвера под все существующие операционные системы и лихорадочно переписывать код при выходе новых версий Windows.
Защиты, основанные на физических дефектах
Идея защит данного типа заключается в умышленном повреждении поверхности диска в одном или нескольких местах. При попытке чтения секторов, попавших в дефектную область, привод, поерзав некоторое время головкой, возвратит сообщение об ошибке. Большинство штатных копировщиков вообще не смогут скопировать такой диск и при встрече с первым же нечитаемым сектором аварийно завершат свою работу. Более "продвинутые" копировщики, умеющие пропускать сбойные сектора, все-таки перепишут всю читаемуюбельную информацию на физически здоровую болванку. Однако, теперь на месте дефектных областей будут располагаться вполне нормальные сектора, свободно читающиеся без каких- лиюбо сбоев (хотя и содержащие "мусор", т.к. скопировать эти сектора копировщику не удалось).
Защите остается лишь проверить подсунутый ей носитель на предмет присутствия физических дефектов в заданном месте и, если сектора с соответствующими номерами читаются без проблем, защита делает вывод, что это не оригинал, а его несанкционированная копия.
Физические дефекты, к которым привязывается защита, как правило, представляют собой крошечные пятнышкие (от одного до двух миллиметров в диаметре) выжженные на его поверхности лазером. При рассматривании диска в отраженном свете они легко обнаруживаются невооруженным глазом (рис. 9.1). На первый взгляд кажется, что точно измерив их геометрические координаты (например, с помощью палетки[9]), нам удастся воспроизвести идентичные повреждение на скопированном носителе. Однако на проверку оказывается, что это не так и причина нашей неудачи заключается в том, что номера секторов никак не привязаны к их физическому расположению. В зависимости от ширины спиральной дорожки, размеров области lead-inLead-In, длины питов и лендов, в одной и той же геометрической точке диска могут находится различные сектора! Вероятность же того, что в двух несвязанных между собой партиях болванок эти сектора совпадут, ничтожно мала! А потому, скопировать такой диск "в лоб" не получится!
Вспомнили? А теперь представьте, что произойдет, если умышленно исказить их так, чтобы даже при успешном чтении сектора возникала неустранимая ошибка контрольной суммы (т. е. такая ошибка, которая не может быть исправлена за счет избыточности). Микропрограмма, "зашитая" в привод, всесторонне проанализировав ситуации ситуацию и даже попытавшись считать "подопытный" сектор несколько раз, в конце концов перестанет ерзать головкой и возвратит сообщение об ошибке, однако, не будет уточнять физическая это ошибка или логическая. А, значит, с точки зрения программного обеспечения и физические, и логические дефекты диска будут выглядеть совершенно одинаково! Вообще-то, защита может попросить вернуть содержимое дефектного сектора в "сыром" (RAW) виде и, если поверхность диска в действительности не содержит никаких физических повреждений, этот сектор будет успешно прочитан, а, раз так, то мы имеем дело не с оригиналом, а копией. Однако не все приводы поддерживают "сырое" чтение (в просторечии называемое "сыром") и потому попытка промышленного внедрения такой проверки вызовет нарекания большого количества легальных пользователей, вызванных несовместимостью с оборудованием, так что прибегать к ней право же не стоит. Разработчики защиты могут сделать "ход конем": если привод не поддерживает "сырого" чтения, то защита доверяет тем показаниям, что у нее есть, в противном случае осуществляется дополнительная проверка. Выходит, что и "волки сыты, и овцы целы!" Скопированный таким образом диск будет работать лишь в тех приводах, что не поддерживают "сырого" чтения.
Наконец, защиту можно просто загрузить в дизассемблер или отладчик, найти процедуру проверки дефектных секторов и "отломать"! Естественно, для этого потребуется знать, как вообще может осуществляться такая проверка, на что в первую очередь обращать внимание и что, собственно, нам следует искать (полный анализ защитного кода — не предлагать).
Операционные системы семейства Windows оказываются на удивление "богаты" в отношении средств работы с дисками CD- ROM на сектором уровне. Помимо очевидных функций CreateFile или DeviceIoControl, существуют такие "приблуды" как ASPI (Windows 9x/NT) и SPTI (только Windows NT), да и другие интерфейсы имеются во множестве. Под Windows 9x можно напрямую обращаться к CDFS-драйверу через функцию ABSOLUTE_READ[10]
прерывания [Y194] [n2k195] INT 2Fh (точнее, не совсем напрямую, а через специальный переходник 16-разрядной динамической библиотеки DLL, вызывающей DPMI-функцию Simulate Real Mode Interrupt[11]; подробнее об этом можно прочитать в технической заметке Q137813 "How Win32 Applications Can Read CD-ROM Sectors in Windows 95" из Microsoft Knowledge Base, входящей в состав MSDN, прилагаемой к диску с Microsoft Visual Studio. Там же содержится исходный текст функции для работы с дисками CD-ROM на секторном уровне).
Под Windows NT чтение/запись секторов осуществляется еще проще. Достаточно открыть диск в cooked-mode[12], после чего с ним будет можно работать на логическом уровне. Образно говоря, все содержимое диска будет трактоваться как один большой файл. Детальное описание этого процесса вы найдете в технической заметке Q138434 "How Win32-Based Applications Read CD-ROM Sectors in Windows NT" из Microsoft Knowledge Base.
Кстати, для проверки наличия дефекта на существование, вовсе не обязательно спускаться на уровень "голых" секторов. Ничуть не с меньшей эффективностью можно воспользоваться и файловым обменом. Очевидно, что файл, содержащий по крайней мере один сбойный сектор не сможет читаться и даст ошибку! Основное достоинство этого способа в том, что его можно реализовать на любом языке, оперируя лишь штатными средствами. Никаких API-функций и "монструозных" IOTCTL[Y196] [n2k197] , — вызова функций fopen/fread будет вполне достаточно!
Таким образом, возможных способов привязки к диску очень много и локализация защитного механизма в коде ломаемого приложения представляет довольно "муторную" задачу. Если точки останова, установленные на функцию DeviceIoContorl не дадут никакого результата (что, впрочем, случается не так уж и часто), то вашему положению трудно позавидовать. Покупайте бочонок пива и занимайтесь с защитой до утра. Да помогут вам API-шпион и контекстный поиск. Ну, со "шпионом", положим все ясно, согласитесь вы. Но поиск-то тут причем? А вот причем: зная номера сбойных секторов, вы можете попробовать отыскать соответствующие им константы в теле программе. Если разработчик защиты не использовал дополнительных ухищрений, то номера контролируемых секторов записаны в программе, так как они есть и все, что вам остается, — поставить на соответствующую ячейку памяти точку останова и немного подождать пока к ней кто ни будь не обратиться. Естественно, если вы исследуете программу не в отладчике, а дизассемблере, то вместо точек останова вам следует использовать перекрестные ссылки.
Если же, несмотря на все усилия, взломать защиту никак не получается, то попробуйте прибегнуть к помощи CloneCD и/или Alcohol 120%. Обе эти утилиты распознают сбойные сектора и имитируют их на логическом уровне. CloneCD — с помощью корректирующих кодов, а Alcohol 120% с помощью корректирующих кодов и виртуального диска! Впрочем, как показывает практика, в подавляющем большинстве случав к виртуальному диску прибегать нет никакой необходимости. Защитные механизмы в своей массе слишком "доверчивы" и искажение контрольной суммы сектора легко вводит их в заблуждение.
Таким образом, стойкость защит данного типа следует считать крайне неудовлетворительной и вам следует хорошо подумать, прежде чем оснащать такой защитой ваши собственные программы. Любой грамотный пользователь, умеющий "держать в руках" CloneCD, скопирует защищенный диск в два счета, и вы останетесь не у дел.
К тому же, для создания качественных физических дефектов ("качественный физический дефект" — это звучит!) требуется труднодоступное и весьма дорогостоящее оборудование, которого у программистов, "пасущихся на вольных хлебах" попросту нет! Царапать же диск гвоздем категорически недопустимо! Это не только ненадежно, небезопасно для здоровья диска CD-ROMпривода, но и не эстетично…
Если же, несмотря на все мои предупреждения, вы все-таки решили остановиться на защите данного типа, то позвольте дать вам несколько советов. Во-первых, наносить повреждения следует не со стороны поликарбонатной подложки, а со стороны защитного слоя. То есть, попросту говоря, сверху диска. Нанося диску повреждения, помните о том, что попытка нанести глубокую радиальную царапину заканчивается, как правильно, очень печально. Диск теряет свою механическую прочность и центробежные силы рано или поздно разрывают его на куски, что практически всегда приводит к гибели и сам привод CD-ROM. Лучше проколите в отражающем слое небольшую дырочку, — того будет вполне достаточно, чтобы один или даже несколько секторов перестали читаться. Осуществить такую операцию можно обычной швейной иголкой.
Теперь поговорим о том, как при нанесении физических дефектов не повредить никаких полезных данных. Царапать не записанную область лазерного диска —бесполезно, все равно он ее не прочтет. Вот и приходится записывать на диск что ни будь не нужное. Такое, "что" не жалко и повредить. Один из возможных способов действия выглядит так.
17.
18. 1) Взяв чистую болванку CD-R, вы "заливаете" на нее все файлы программы, за исключением того, что содержит защитный механизм привязки к диску. Как правило, это главный исполняемый файл программы, хотя защита вполне может быть помещена и в одну из динамических библиотек или даже внедрена в файл данных, но это уже "извращение".
19. 2) Нажмите на кнопку выброса диска и каким-либо образом пометьте положение последней записанной дорожки (например, измерьте диаметр "прожженной" области обычной ученической линейкой).
20. 3) Верните диск на место и запишите на него примерно 150 Мбайт всякой всячины, которая и послужит плацдармом для царапанья. Закрывать сессию все еще не надо!
21. 4) Теперь нанесите диску одно или несколько физических повреждений, тыкая иголкой в область последнего записанного кольца.
22. 5) Запустите любой "дисковый доктор" и определите позиции всех обнаруженных сбойных секторов;
23. 6) "Прошейте" номера сбойных секторов в защищаемую программу и "залейте" защитный модуль на болванку, закрыв за собой сессии.
24. 7) Все! Защита готова!
Защиты, основанные на привязке к носителю
Считается, что правильно снятая цифровая копия целиком идентична своему оригиналу, благодаря чему, собственно, разница между пиратскими и фирменными дисками заключается лишь в их стоимости и потребителю не приходится ломать голову над вопросом чью продукцию покупать. На самом же деле, двух абсолютно идентичных лазерных дисков не существует,— каждый из них обладает рядом уникальных характеристик, которые отличают его от других. Эти уникальные характеристики (далее по тексту — метки) могут использоваться защитными механизмами для идентификации оригинального носителя и разоблачения несанкционированных копий.
Профессиональная этика обязывает использовать для идентификации лишь те метки, которые отвечают следующим, достаточно жестким, требованиям:
q метка должна безошибочно распознаваться любыми приводами;
q метка не должна воспроизводится какими бы то ни было копировщиками;
q устойчивость метки к внешним воздействиям (царапинам, старению диска) должна быть не ниже, чем у остальных данных, записанных штатным способом.
К сожалению, защиты, удовлетворяющей всем вышеперечисленным критериям, до сих пор не существует и достаточно часто приходится сталкиваться с тем, что легально купленная программа ошибочно считает себя пиратской копией и потому отказывается запускаться. Какие же характеристики носителя разработчики защит выбирают в качестве ключевых? Анализ существующих защит показывает, что это: во-первых, физические дефекты носителя (как естественные, так и умышленно нанесенные); во-вторых, временные характеристики чтения групп секторов; в-третьих, показатель стабильности чтения, и, в-четвертых, та информация, которую сообщает сам носитель (ATIP в частности).
Защиты, основанные на "слабых" секторах
"Столкновение" с этими защитами поразило даже меня, видавшего виды хакера. Да как тут было не поразиться! Судите сами — копирование защищенного диска происходит без ошибок, но при проверке копии обнаруживается множество сбойных секторов, которые появляются даже в том случае, если содержимое оригинального диска пофайлово перегнать на винчестер и уже оттуда записать его на болванку CD-R. Что это: неисправность оборудования или результат работы хитрого драйвера, скрыто установленного защитой при первом запуске защищенной программы? Но нет, — все оборудование полностью исправно и никакие драйвера тут не причем, сбойные сектора возникают даже при копировании диска на заведомо "стерильной" машине. Руки, естественно, так же "не кривые" и голова "не дырявая".
Исследование самих копируемых файлов под отладчиком (HEX-редактором, дизассемблером) так же не выявляет ничего необычного и если "отломать" защиту от привязки к CD (при условии, что она там есть), то защищенное приложение будет успешно запускаться с жесткого диска (Zip-накопителя), но вот "закатать" его на болванку, увы, не получится. Правда, если защищенные файлы каким-либо образом "подрихтовать" (например, сжать любым архиватором), то перенос на болванку CD-R пройдет без ошибок, но… ведь это уже совсем не то, верно?
Таким образом, причина столь странного поведения защиты лежит отнюдь не на программном, а физическом уровне. Это самый "крутой" антиотладочный прием, которой мне только доводилось когда-либо встречать! Действительно, с точки зрения хакера, не обремененного сложным измерительным оборудованием, привод CD-ROM представляет собой "черный ящик", работающий приблизительно по такому же принципу, как и любой другой накопитель. Даже если снять с него крышку, мы все равно ничего не увидим кроме переплетений проводов и нагромождений микросхем. Единственное, что остается — вдумчиво перечитывать стандарты. Ведь если защита работает на всех (или хотя бы подавляющем большинстве) моделей приводов CD-ROM, то она должна обязательно опираться на те или иные стандартные свойства/особенности/характеристики!
Вот, допустим: " A regular bit pattern fed into the EFM encoder can cause large values of the Digital Sum Value in case the merging bits cannot reduce this value (see annex E). The scrambler reduces this risk
by converting the bits in byte 12 to 2 351 of a Sector in a prescribed way" ("Регулярные битовые шаблоны, "скормленные" EFM-декодеру могут вызвать большие значения DSV, неустранимые merging-битами. Скремблер сокращает риск этого путем преобразования бит с 12- по 2.351 байт сектора определенным образом")[13]. Если вы все еще продолжаете думать, что диски CD-ROM идеальные носители для хранения исполняемых файлов и баз данных, то вы глубоко заблуждаетесь. Лазерные диски изначально разрабатывались для записи музыки и лишь ценой больших инженерных "извращений" согласились хранить бинарные данные. Обратите внимание на выделенные полужирным шрифтом слова. Скремблер не гарантирует, что записанные данные смогут читаться, он лишь снижает риск образования неблагоприятных (с точки зрения привода) последовательностей до разумного минимума. Тем не менее, если постараться, то вполне можно создать пару файлов, напичканных этими самыми наиболее неблагоприятными последовательностями по самую завязку. Теоретически их можно прочесть, но с этим справятся лишь наиболее качественные модели приводов CD-ROM, а остальные дадут ошибку.
Рассмотрим следующую комбинацию: 04 B9 04 B9 04 B9… Обратившись к таблице EFM-перекодировки найдем, что 04 преобразуется в 01000100000000, а B9 — 10000000001001. Попробуем теперь их записать вместе: 01000100000000 ххх 10000000001001 yyy 01000100000000, где xxx и yyy — merging-биты. Поскольку, 04 содержит в своем "хвосте" восемь нулей, а B9 начинается с единицы, то единственной возможной комбинацией для первой партии merging-бит будет 100. Соответственно, поскольку B9 кончается на единицу, а 04 имеет в своей голове всего один нуль, то единственно подходящей комбинацией для второй партии объединяющих бит окажется 000.
А теперь изобразим это графически и вставим разработчикам CD-ROM хороший "пистон".
Смотрите (см. рис. 9.70х0024)! Значение DSV оказывается резко отрицательным! То есть, попросту говоря, питыpit'ы преобладают над лендамиland'ми и поверхность диска становится темной как "негр". Как следствие —– следящее устройство сбивается с дорожки из-за недостаточного количества света, попадающего в фотоприемник. Самое интересное, что по стандарту такие комбинации битов читаться и не обязаны (хотя некоторые модели приводов с ними все-таки справляются). Вот вам и надежное устройство для хранения данных! В силу природы своих конструктивных особенностей, лазерные диски способны хранить лишь некоторые.
Рис. 9.7. унок 29 0х024 Физическое представление последовательности 04 B9 04 при записи
Конечно, если просто создать файл, битком надбитый "\x04\xB9\x04\xB9…" его запись на болванку CD-R и последующее чтение пройдут без проблем, ведь поток записываемых данных предварительно обрабатывается скремблером! Грамотно выбранный алгоритм скремблирования не должен допускать эффективного обращения, в противном случае злоумышленник может прогнать наиболее неблагоприятные регулярные последовательности через "антискремблер" и тога при повторном скремблировании они запишутся на диск в своем исходном виде. Теперь собственно "пистон". Алгоритм скремблирования, используемый приводами CD-ROM, такое обращение как развполне допускает! Все, что понадобится дляна написания антискремблера —– это пара вечеров свободного времени и сам стандарт ECMA-1320[Y199] [n2k200] . Поскольку, алгоритм скремблирования базируется на функции XOR, то повторное скремблирование уже обработанных скремблером данных, возвращает их в исходный вид. Благодаря этому обстоятельству, мы сможем обойтись и одной функцией —– функцией скремблера.
Прогнав через скремблер защищенные файлы мы с не без удовлетворения обнаружим, что они содержат они по крайней мере одну очень неблагоприятную последовательность, значение DSV которой резко отрицательно (реже —– резко положительно).
В общем, категорически не равно нулю. Дополнив скремблер функцией подсчета DSV (подробности о ее реализации см. в ECMA-1320), мы получим автоматический сканер защит, основанныйх на "слабых" секторах. Wow! Разве не "круто"?! Если внутри защищенных файлов обнаружатся те или иные неблагоприятные последовательности, то можно и не пытаться их копировать на болванку CD-R —– все равно из этой затеи ничего не получится.
Хорошо, но как нам объяснить тот факт, что эти же самые неблагоприятные последовательности успешно читаются с оригинального диска?! Чтобы ответить на этот вопрос, нам придется забраться в самые дебри спиральных дорожек лазерного диска. Труден и опасен будет этот путь! Вас станут отговаривать и предостерегать. Вот, например, цитата из одного реферата неизвестного автора: "…на самом деле все еще интереснее, т. к. в дополнение к секторам определяются секции того же полезного размера, но с несовпадающими границами, причем часть адресов является адресами секторов, а другая —– адресами секций. Но об этом лучше сразу забыть .;)". "Смайлик" (символ с улыбочкой) на конце этих строк очень сильно возбуждает и заставляет перечитывать и перечитывать сухие строчки стандарта (поскольку, ничего более информативного под рукой, увы, нет). Так или иначе (на худой конец обратитесь посетитексм. форуму на сайте http://club.cdfreaks.com) мы выясним, что границы секторов и фреймов могут и не совпадать, —– сектор может начинаться с 0, 4, 8, 12, 16 или 20-го байта по счету! Изменение стартовой точки неизбежно изменяет и DSV первого фрейма, —– вот тут-то и начинается самое интересное. Если количество бинарных единиц фрейма нечетно, то второй фрейм инвертируется (то есть питыpit'ы и лендыland'ы меняются местами), в противном случае последующий фрейм идет так, как он есть. Благодаря этому становится возможным подобрать такую регулярную последовательность, которая окажется вполне благоприятной для одной из точек входа, и крайне неблагоприятной для всех других.
К сожалению, пишущие приводы"писцы" все еще не позволяют выбирать произвольную точку входа и назначают ее самостоятельно по своему усмотрению. Хорошие пишущие приводыписцы (типа Plextor) выбирают точку входа так, чтобы значение DSV сектора было минимальным по модулю (и потому позволяют копировать защищенные диски без проблем). К сожалению, подавляющее большинство остальных моделей слишком "тупы" и задача минимизации DSV им оказывается "не по зубам". Они либо вовсе не пытаются вычислить правильную точку входа, либо же вычисляют ее неправильно. Как следствие —– при чтении скопированных защищенных дисков возникает ошибка!
Тем не менее, "продвинутые" копировщики (к которым, в частности, относится Clone CD) такие защиты уже давно обошли. Как они ухитряются это делать? Подготовив образ сектора для "нарезки" в "сыром" виде, они слегка уродуют его содержимое, рассыпая тем самым неблагоприятные последовательности "в пух и прах" (искажение одного бита исходных данных вызывает драматические изменение данных после скремблирования!). Корректирующие коды (ранее подготовленные для не изуродованного содержимого сектора) при этом не изменяются. В результате, чтение записанного таким образом сектора дает ошибку и приводу приходится ее исправлять по избыточной информации, содержащейся в корректирующих кодах. После исправления, сектор вновь возвращается в неискаженный вид.
Достоинство такого подхода заключается в том, что копия защищенного диска, содержит ослабленный защитный "организм", свободно дублируемый в "сыром" режиме. Штатное копирование, правда, будет приводить к ошибке, поскольку честные копировщики помещают на диск уже "исправленные" сектора. С другой стороны, копировщики, записывающие сектора "как они есть" оказываются не в состоянии отличить умышленные искажения от простых физических ошибок чтения. Запись неисправленного сектора приведет к увеличению числа ошибок, и при попытке снять копию с копии мы рискуем получить вообще не читаемый дубликат , (что есть большой недостаток!).
Какой из этого выход? Читаем сектор, исправляем его, прогоняем через сканнер и, если в нем обнаружитьсяобнаружится так или иная неблагоприятная последовательность, умышленно изменяем в нем несколько бит. Таким образом, ошибки копирования больше не будут накапливаться!
[1] ATIP (Absolute Time In Pre-Groove) — информация о реальном производителе CD-R/RW носителя, максимальной разрешенной скорости записи и максимальную емкость диска. — Ред.
[2] EDC/ECC (Error Detection Code/Error Correction Code — коды обнаружения и исправления ошибок. — Ред.
[3] Автор имеет в виду Mac OS (Macintosh Operating System) — операционную
систему фирмы Apple Computer для ПК Macintosh — Ред.
[4] в Красной Книге, соответствующей стандарту ECMA-130, данное поле именуются FRAC, а в SCSI Multimedia Commands/ATAPI DVD Devices – Frame, что создает определенные неудобства и терминологическую путаницу.
[5]
см. также техническую заметку Q137247 из MSDN "IOCTL_SCSI_MINIPORT and IOCTL_SCSI_PASS_THROUGH Limitations"
[6]
"Приблизительно" —– потому что на некоторых приводах Ahead Nero Нерон вообще ничего не выдает
[7]
"Древние" приводы были слишком простыми устройствами и на декодирование субканальной информации — — между прочим, размазанной по большой поверхности диска! — им требовалось определенное время, в течении которого оптическая головка успевала отъехать по спиральной дорожке далеко в строну, и потому при попытке позиционирования на начало трека, какое-то количество секторов оказывалось неизбежно пропущенным! Вот и пришлось добавить к началу всякого трека несколько витков "пустой" информации, чтобы компенсировать выпадения значащих секторов
[8] vs. — versus
(лат.) — против. — Ред.
[9]
Палетка (от франц. palette — пластинка, планка), начерченная на прозрачной бумаге, стекле или целлулоидной пластинке сетка линий, образующих квадраты известных размеров, при помощи которых определяется площадь участков на плане или карте. — Ред.
[10]
EAX := 1508h см. Interrupt List By Ralf Braun
[11]
Кстати, это очень хорошая функция и с ее помощью Win32-приложения могут сделать все то, что разрешается делать приложениям MS-DOS (бесконтрольный доступ к оборудованию, прерыванием и все такое).
[12]
Открытие диска в cooked-mode открытие осуществляется посредством функции CreateFile, вызываемой следующим образом: hCD = CreateFile ("\\\\.\\X:", GENERIC_READ,FILE_SHARE_READ| FILE_SHARE_WRITE,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);, где X — буква, обозначающая логический диск
[13]
Выделение полужирным шрифтом сделано автором книги. — Ред.
[Y1]Здесь и далее, если это обозначение, то нужно привести полное написание копировщика. Хотя, на сколько я понимаю это полное название.
[n2k2]да, это полное его название
[Y3]Да, на названном вами сайте название приведено без пробела, т. е. как CloneCD. Наверное по всей книге следует поменять на такое написание или ваше правильное?
[n2k4]в разных источниках CloneCD именуются по разному…
[n2k5]заключат что?
[n2k6]просьба убрать отсебятину и вернуть все в исходный вид. Информация на гибком магнитном диске (флоппи диске, дискете) содержится на концентрических окружностях, называемых дорожками (tracks), расположенных на обеих сторонах дискеты и обслуживаемых головками чтения - записи (heads). Нижняя сторона дискеты обслуживается головкой 0, верхняя - комбинация из двух противолежащих дорожек на разных сторонах называется цилиндром (cylinder).
[n2k7]а чем "резец" не нравится?
[n2k8]"сам-то понял, что сказал?" (с) анекдот.
вернуть обратно
[Y9]Или я не врав в сноске это носитель ATIP?
[n2k10]да сносите пожалуйста ;)
[n2k11]тогда уж лучше дать ссылку на соответствующую главу книги, а вообще EDC/ECC это общепринятый термин.
[Y12]Может быть следует сделать пояснение (в виде сноски, примечания или как-то иначе) что такое MS WDB?
[n2k13]Microsoft Windows Debugger
[n2k14]краткость – сестра таланта, речевая избыточность – саксь и маст дай.
[n2k15]почему с "лазерных дисков", а не "дисков лазерных"? "жестких дисков", а не "дисков жестких"? "CD- ROM диски" звучит намного приятнее уху, а "с диском CD-R" это ни выговорить, ни прочитать ;(
[n2k16]это не повтор слов, это так задумано ;)
[n2k17]ну не говорят так! cdfs-драйвер, ndis-драйвер, видео-драйвер…
[n2k18]нету такого сектора!
[n2k19]замечательно! directory у вас переводится как каталог, а catalog тогда что? тоже каталог? или директория? вернуть обратно как было
[Y20]"Ниспадающий бокс" это "Выпадающее меню" или "Раскрывающийся список" уточните что это?
[n2k21]dropdown
[n2k22]это они (треки) в сессии, но сессия "их".
[Y23]Впереди поставьте уточняющее слово (поле, значение или?). Или это в смысле "сессия"? Исправьте как надо. Если это программный элемент, то следует выделить стилем "Courier".
[n2k24]можно и курьером. session – поле, но это искажает смысл. "поле Session" никогда не бывает последним, а вот session – очень даже может быть!
[n2k25]покажите мне "area lead-in" и я съем свою шапку. почему "выводная область", а не "область выводная"?
[n2k26]грубая ошибка. тогда уж не области, а указателя на.
[n2k27]здесь только полям
[n2k28]ну вот, уже дисководом обозвали, так мы и до перфоратора доберемся
[n2k29]оптимальный это по определению наилучший. поздравляю. вы как редактор в моих глазах очень возросли. ладно бы вы это просто пропустили, но вы исправили правильное, на заведомо ложное. короче говоря, ничем иным кроме нахальным издевательством над собой я это объяснить не могу, а потому за себя не отвечаю!
[n2k30]это действительна та Джульета из той бессмертной пьесы.
[Y31]На рисунке 1.8 обозначено P-FRAC и соответственно A-FRAC.
В таблице все верно?
[n2k32] добавил сноску
[Y33] Проверьте нумерацию битов на рисунке. На мой взгляд на рисунке ошибки. 96, указанных на рисунке бит, должно быть распределено так: 0—3, 4—7, 8—79, 80—95.
[n2k34]рисунок выполнен в полном соответствии со стандартом ECMA-130 (см. стр. 30). 0 и 97 это не номера битов, а границы пакета, так что здесь все правильно.
[Y35]Это фреймо-секунды, просто фреймы или у вас все верно?
[n2k36]да, секунды здесь лишние
[Y37]Распишите аббревиатуру MSF и LBA (сделайте пояснение).
[n2k38]MSF – Минуты Секунды Фреймы, LBA – Logical Block Address, в тексте кстати говоря, такая расшифровка есть, только на ней не акцентируется внимание.
[n2k39]не четыре, а четырнадцать ;) границы EFM-слом никак не размечены и данные представляют собой сплошной битовый поток…
[n2k40]CloneCD это уже имя собственное, зачем же постоянно добавлять уточняющее существительное?
[n2k41]если уж вы исправляете "любопытное" на "любопытная", то нужно изменить и "одно". это во-первых. теперь по вторых: скажите, пожалуйста, а каким таким образом можно отличить экспортируемую функцию от… экспортируемой таблицы например? мы имеем лишь public-имя, которое может быть всем, чем угодно, и "функция" появляется позднее, когда мы уже заглянули в дизассемблер…
[n2k42]"Эдик пошел за пивом". обязательно что ли добавлять "человек Эдик"? ;)
[n2k43]система или шифр Вернама, код описывает правила построения алфавита, шифры работают с отдельными битами, буквами, символами. коды оперируют
лингвистическими элементами (слоги, слова, фразы).
[n2k44]"полученные дизассемблером из того же Clone CD". морковку с грядки вы тоже получаете? ;)
[n2k45]"выполнение операции идет кусками" это еще более неправильно, чем "ксоренье", тогда уж "то, что скремблирование осуществляется 32-битными…"
[n2k46]скрипит прочно вошел в программистский язык, так что можно давать его и без перевода
[n2k47] слишком длинно. русский язык _позволяет_ создать глагол от XOR, и пренебрегать мощью великорусского языка нехорошо. "ксорит" сейчас обще употребляемый термин. язык это не застывшая статуя и не икона на стене. это весьма подвижная и непрерывно изменяющаяся сущность…
[n2k48]может быть, тогда и 2 + 2 записывать как + 2 и 2?
[n2k49]насколько "недавно"? ;)
[Y50]На рис. 1.20 не обозначено "encoder C2". Есть только подпись "C1 encoder". Проверьте нет ли ошибки.
[n2k51]да, здесь действительно была ошибка
[Y52]Распишите аббревиатуру и поясните что это такое.
[n2k53]program [memory] area. описана ниже
[Y54]Распишите аббревиатуру и поясните, что такое ATIP. Во введении я привел это именно то или? "ATIP (Absolute Time In Pre-Groove) — информация о реальном производителе CD-R/RW носителя, максимальной разрешенной скорости записи и максимальную емкость диска".
[n2k55]вы совершенно правы
[n2k56]gnu это "Gnu Not Unix", это не стандарт. и ни каким боком не бесплатного. свободного – это да. но эта свобода очень сильно напоминает свободу тов. Сталина, лицензий в рамках GNU-сообщества куча (эта вроде GPL), но мне в лень в них разбираться, ибо я не юрист по профессии и ничерта там все равно не понимаю, к тому же на территории РФ никакой силы они не имеют.
[Y57]Что такое GNU? Может быть следует сделать примерно такую сноску: "Слово GNU понимается как стандарт бесплатного программного продукта, а GNU лицензия — это лицензия, распространяемая с подобным программным продуктом".
[n2k58]контр пример: "советская схема" (например, телевизора), или "схема советов"
[n2k59]кончайте издеваться, да?!
[Y60]Проверьте нет ли ошибки в написании названия кодов.
[n2k61]а хрен его знает, он в разных источниках зовется по разному
[Y62]Что это? Приведите электронный адрес.
[n2k63]бросайте свой каменный топор и слезайте с медвежьей шкуры… оглянитесь вокруг… уже все давно знают, что слово "гуглить" обозначает "поиск в интернете"…
[Y64] При ссылке на книгу или статью надо так и писать, указывая при этом: издательство, год и может быть количество страниц.
[n2k65]кто вам сказал?
[Y66]После листинга в тексте или в стиле "Замечание" поясните, что такое "стандарт ECMA-130". Возможно распишите аббревиатуру.
[n2k67]раньше уже объяснилось. сколько можно?!
[Y68]Проверьте запись. Нет ли ошибки? Что означает "+=" ? Кроме того, проверьте в листинге 1.17 запись "++i".
[n2k69]ошибок нет
[Y70]Проверьте ссылку.
[Y71]Проверьте ссылку. Была ошибка.
[Y72]Проверьте ссылки на рисунки был сбой в нумерации.
[Y73]Поясните, что это такое и почему с заглавной буквы, если это название накопителя?
[n2k74]все правильно
[Y75]Что это за имя такое. Здесь нет ошибки?
[n2k76]все правильно
[Y77]Впереди поставьте определяющее, уточняющее слово. Наверное это функция, а может быть и команда?
[n2k78]а может быть и макрос ;) вам оно надо? если GetProcAddress ведет себя как GetProcAddress – какая хрен разница что это такое…
[Y79]Аналогично предыдущему вопросу.
[n2k80]аналогично предыдущему ответу
[Y81]Не очень удачное название раздела
[n2k82]не хуже всего остального
[n2k83]ага, сразу после главы посвященной ECC/EDC кодам дается их расшифровка. для склеротиков, не иначе…
[Y84]Ссылка должна бать сделана, например, так: "см. раздел "Доступ посредством ASPI" главы 2". Или удалите эту ссылку в не куда.
[n2k85]выкиньте свой англ. нахрен или не пытайтесь им щелолять. врапер по жизни был оберткой. даже на курсах английского рассказывают анекдоты про мексиканцев, которые едят сникерсы вместе с врраперами
[Y86]Ссылка сделана не верно. Нужно указать название раздела и номер главы или удалить ссылку.
[Y87]Выделения сделаны красным? Почему? Если надо выделить, то выделяете полужирым.
[Y88]В тексте нет ссылки на листинг.
Допишите какой- то переходной текст перед шапкой листинга 3.4 и вставьте ссылку.
[Y89]Что значит с нашего сервера? Наверное сервера автора? Кроме того нужно указать адрес сервера. А почему бы эту программу не поместить прямо на компакт-диск к этой книге?
[Y90]В тексте поясните, что означает выделенная строка в листинге 3.5.
[n2k91]очевидно это место сбоя.
[Y92]Проверьте. Нет ли ошибки. Хорошо бы сделать уточнение в стиле "Примечание". Кроме того, если все верно внесите в индекс "Ординалы".
[n2k93]ошибки нет.
[Y94]Поясните что это за понятие. Или ошибка "золоченные" хотя и это тоже не понятно.
[n2k95]все правильно
[Y96]Желательно написать издательство, год издание и количество страниц.
[n2k97]смысл?
[Y98]На следующие два рисунка 3.5 и 3.6 в тексте нет ссылок. Вставьте ссылки и, возможно, переместите рисунки сразу же после ссылок.
[n2k99]ссылок нету и не будет.
[Y100]Все функции, команды (все что относится к программе) выделяется Courier. Здесь это что? Нужно выделять или нет?
[Y101]Не знаю надо ли выделять курьером (Courier) значения? Если это относится к программному коду, то надо.
[Y102]Впереди необходимо поставить поясняющее слово (оператор, функция или …). Соответственно выделять курьером или нет?
[Y103]Как правильно пишется Sense Info? (У Вас: то "sense info", то "Sense Info", то "SENSE INFO". Надо сделать хотя бы одинаково по всей книге. Как правильно?). одинаково не получится, в разных источниках оно пишется по разному
[Y104]В таблице некоторые ячейки выделены серым цветом. Надо пояснить, что это выделение обозначает.
[Y105]Что такое "бряк"? Если это жаргон, то как это на нормальном языке?
[n2k106]жаргон. как на нормальном языке не скажу, иначе вы опять возьме это в круглые скобки.
[n2k107]это и ниже когда-то было подрисуночной надпись.
вернуть на место все как было…
[Y108]Распишите в скобках аббревиатуру.
[n2k109]что такое аббревиатура? моя не грамотная, моя вас не понимает.
[n2k110]вы это… прежде чем править правильное на неправильное, вы хотя бы посмотрели как сама M$ его пишет, вот вставляю из буфера обмена " Windows Me DDK", как вы видите 'e' строчная. вернуть всю "ME" на "Me"
[Y111]Распишите аббревиатуру. Что это? Документ или что-то другое. Напишите определяющее слово.
[n2k112]если аббревиатуры постоянно расписывать, зачем они нужны? скажите, вы считаете, что фраза "обратитесь к Библии". что, нужно обязательно добавлять, обратитесь к книге Библии, при том, что библия это вообще-то книнга, а MSDN это Network – т.е. определяющее слово уже входит нее!
[n2k113]нахрена расшифровывать общеупотребимые сокращения?!
[Y114]Добавьте поясняющее, уточняющее слово. Выделять ли это Курьером?
[n2k115]новая языковая находка! поздравляю!
[n2k116]ну нихрена же себе! еще и расшифровка и перевод. это для особо тупых да?!
[Y117]"В-четвертых" либо пропущено, либо этот пункт должен быть "в-четвертых".
[Y118]Написание структур у Вас везде в верхнем регистре, а здесь нет. Это верно?
[n2k119]все правильно
[Y120]LU это Logical Unit или что-то другое? Поясните.
[n2k121]там написано
[Y122]На рисунок в тексте книги нет ссылки. Вставьте.
[Y123]Перед этим требуется поясняющее слово (документ, каталог или …?).
[n2k124]тут же написано "см.", т.е. "смотри", вот посмотрите и узнаете что это такое ;)
[n2k125]интерфейс вы поставите сами в своем редакторском примечании. пусть над вами все смеются, но нехрен меня выставть идиотом. вы хоть оригинал смотрели? "…this specification describes the API…" где вы здест видите интерфейс?!
[n2k126]я же просил вас это убрать. просьбы на вас, как видно не действуют ;(
[Y127]Напишите уточняющее, определяющее слово.
[n2k128] не напишу, ибо это будет слоблудие, граничащее с речевым поносом
[Y129]Вставьте уточняющее, поясняющее слово.
[n2k130]учите английский. он рулез. там же написано черным по белому. спецификатион. нахрена повторить это два раза?!
[n2k131]проснулись! поздравляю!
[n2k132]потрясающая врезка! а что "внимание" в тексте оставит была не судьба? а такое крылатое выражение как "по причинам не зависящим от", вам не говорит, что "указатель на" это не ошибка, а так задумано?
[Y133]Добавьте уточняющее, поясняющее слово (тело файла или ?).
[Y134]Добавьте уточняющее, поясняющее слово. Если это относится к программу коду, то нужно делать в стиле Courier.
[n2k135]см. в словарь. оксфордовский. а потом спрашивайте.
[Y136]Добавьте поясняющее слово.
[n2k137]там их целых два. даже три! мало?!
[Y138]Распишите аббревиатуру.
[n2k139]не расшишу. нахрен мне надо чтобы надо мной смеялись? вы бы еще КПД попросили расписать.
[Y140]Впишите поясняющее слово.
[n2k141] рррр!
[n2k142]"вошел в двом посредством двери". всем так и буду говорить, что одни знакомый редактор утвердал, что именно такой путь правильный, а "вошел в дом через дверь" – неверно.
[n2k143]да… плохо не знать английский… скажите мне, а чем "Interrupt list" от "списка прерываний отличается"?
[n2k144]позвольте мне настоять за слове "задница", это уже в конце вопрос принципа.
[n2k145]если вы не понимаете юмора, то это не значит, что его не понимают и остальные. "оно" это то, которое не тонет.
[Y146]Наверное не помешает написать где этот список взять.
[n2k147]догадайтесь с трех раз ;) в гугле набить религия не позволяет, да? кроме того, что у тех, кому он нужен, он уже есть
[Y148]Поставьте впереди уточняющее поясняющее слово (функция, команда или …).
[n2k149]можно обойтись и без поясняющих слов, ибо и так все понятно.
[n2k150]теперь я понял почему windows такая большая ;(
[Y151]Почему все на русском языке? Программа рускоязычная или нет? Если нет, то нужно привести истинные названия вкладок, меню и т. д.
[n2k152]программа поддерживает многоязычный интерфейс
[n2k153]архиватор вообще-то это tar и от него производные. тот факт, что zip, arj и другие совмещают в себе функиции архиватора и упаковщика еще не дает право называть чистый упаковщик архиватором.
[n2k154]вот и soft-ice архиватором обозвали ;(
[n2k155]на кой хрен такой перевод?! вы еще в римские цифры переведите – от них больще пользы.
[n2k156]блесяще!
[n2k157]вообще-то 16, но все равно нахрен?!
[Y158]Как правильно называется программа? У Вас то "Now", то "NOW", то без восклицательного знака, то с ним. Я нашел такое "Stomp RecordNow MAX" может быть это верное. Должно быть везде одинаково и желательно официальное верное название (как на сайте производителя). Это касается всех названных программных продуктов. Проверьте и где надо исправьте.
[Y159]Хорошо бы расписать и RAW.
[n2k160]за этим в оксфордовский словарь
[Y161]Ссылка на что? Листинг, раздел, главу? Какие? Где?
[Y162]Было в байтах. Судя по листингу Кбайт. В листинге верно или?
[n2k163]Кбайт
[Y164]Здесь пропущено слово "из строя"? или?
[Y165]Ссылка на что?
[Y166]Что это за запись? Если листинг, то должно быть название.
[Y167]Название истинно? Или "прикол"?
[n2k168]жаргон-с
[Y169]Это программа, утилита или?
[n2k170]чем программа отличается от утилиты?
[n2k171]с каких это пор играются поврежденные mp3 файлы? ;)
[n2k172]что за формат дисков с mp3? какой комитет его курирует?
[n2k173]не надо править то, что в чем не разбираетесь или по крайней мере внимательно читайте словарь. "читающие устройство" – это cd-rom или типа того. "read engine" это компонент программного обеспечения блока микропроцессорного управления этого самого cd-rom'а ответственный за декодирование структур данных высших уровней абстракции.
на русский язык это не переводимо (во всяком случае нормативным языком).
[Y174]Флажок верен? Проверьте.
[n2k175]сойдет и "флажок"
[Y176]Сюда не плохо бы вписать сообщение об ошибке вместо многоточия или вообще удалить ссылку на программу Nero.
[Y177]Поменял X на N и Y на M чтобы не путать ранее встречающийся "X-сектор" c нынешнем "сектор Х".
[n2k178]ок
[Y179]В текст нужно обязательно вставить ссылку на рисунок, такую "(рис. 6.23)".
[Y180]Уточните ссылку. Может это разд. "Защиты, основанные на "слабых" секторах" 9-й главы? Или я не прав? Вобщем уточните.
[Y181]Может "прожига", а то получается "масло масленное".
[n2k182]"прожигать" привод нельзя, можно "прожигать" ПЗУ, но "прожигаемые" ПЗУ в приводах не применяются. а по поводу масла – тут виноват не я, а архитекторы приводов!
[Y183]Такого раздела нет. Может имеется в виду разд. "Защиты, основанные на "слабых" секторах" главы 9 или разд. "F1-фрейм" главы 1, где есть упоминание о слабых "секторах"?
[Y184]Далее в скобках приведите перевод или лучше напишите по-русски, а англ. возьмите в скобки.
[n2k185]адекватный перевод на русский отсустует.
[Y186]Здесь было что-то пропущено или все верно?
[n2k187]все ок
[Y188]Проверьте название. Оно должно быть точным — вплоть до регистра (верхний или нижний) слов наименования.
[n2k189]проверил – все ок.
[Y190]Наверное, имеется ввиду утилита из Norton Utilities "Disk Doctor". В любом случае следует сделать пояснение в сноске.
[n2k191]не обязательно нортон, докторов было много…
[Y192]В названии файла видимо опечатка должно быть не "rask.iso", а "track.iso"?!
[n2k193]да, вы правы
[Y194]Поясните пожалуйста текст приведенный в сноске.Что там за ссылка и на что она сделана?
[n2k195]
знаменитый Interrupt List от Ральфа Брауна – у всякого хакера он всегда под рукой. прерывание известно, функция указана – а большего и не надо
[Y196]Что такое IOTCL? Желательно пояснить и добавить уточняющее слово.
[n2k197]IO-ConTroL, вообще-то это коды (управляющие), а пояснения даны в главе, посвященной интерфейсам взаимодействия. если все сокращения постоянно расшифровывать то на кой они нужны?!
[Y198]Кого ее? Напишите конкретно.
[Y199]Исправил на 130. У Вас было 120. Верно сделал?
[n2k200]да, вы правы
Защиты, основанные на временных характеристиках чтения
Скоростная диаграмма чтения, — вероятно наиболее легко измеряемая, но в то же время и наиболее уникальная характеристика диска, значительно варьирующаяся от одной болванке к другой. Проведем простой эксперимент: возьмем какой-нибудь компакт-диск, сделаем его копию, а затем сравним скоростную диаграмму чтения копии и оригиналом. Результат, полученный автором, иллюстрируют два следующих рисунка, приведенные далее (в роли эталонного диска выступил сборник альбомов "Агаты Кристи", в роли пишущего привода — PHILIPS CDRW 24000; за построение графиков и копирование дисков отвечала программа Alcohol 120%, а само копирование осуществлялось на болванку Imation 48x).
а
б
Рис. 9.2.унок 24 0х026,0х027 Скоростные диаграммы чтения оригинального диска (а) и его копии (б)
Почувствуйте, как говорится в одной рекламе, разницу! Не все диски одинаковы! Причем, они очень сильно неодинаковы, — и отличия между ними видны даже "невооруженнымому глазому!"! Как это можно использовать для защиты программ? Выделяем на временной кривой ряд узловых точек, которые соответствуют "пикам", "провалам" или отсутствию таковых на заданном отрезке. Затем преобразуем их в код характеристик, памятуя о том, что в процессе эксплуатации диска (а так же чтении диска в других приводах) профиль кривой будет значительно меняться: одни изломы могут исчезать, а другие — появляться. Поэтому, для идентификации диска придется применить алгоритм нечеткого сравнения, то есть даже при совпадении нескольких узловых точек диск считается оригинальным. Конечно, чем мягче критерии отбора, тем больше вероятность того, что "левая" копия будет принята за правильную. Чрезмерно жесткие критерии, напротив, вызывают "угарный мат" тех пользователей, "раздолбанные" приводы которых исказили временную кривую настолько, что оригинальный диск неожиданно перестал восприниматься ими. По опыту автора, хороший баланс между надежностью и стабильностью представляет отношение 3:10 — т. е.
если распознаны хотя бы три узловые точки из десяти, то диск считается оригинальным, ну и соответственно наоборот. В конечном счете, пусть лучше защита не заметит факта своего копирования, чем "обругает" законного пользователя.
Кстати, для построения временной диаграммы вовсе не обязательно работать с диском на сектором уровне, — ничуть не худший результат дает измерение скорости чтения отдельных файлов шатанными средствами (например, функцией frerad языка Си). Разумеется, это должны быть большие файлы. Настолько большие, чтобы гарантированно не уместиться в кэше и заставить операционную систему обращаться к диску, а не брать эти файлы из оперативной памяти. Минус этого решения в том, что для построения временной кривой вам потребуется прочесть, по крайней мере, половину диска (а это — время), поскольку "разрешающая" способность у файлового "измерителя" очень низка. С другой стороны, вам ведь все равно придется читать записанные на диск данные, не правда ли? так почему бы тогда не совместить приятное с полезным?!
Если же вы "закатываете" на диск пару небольших утилит, то, конечно, для привязки к диску лучше воспользоваться замером времени чтения отдельных секторов. Поскольку, в силу несовершенства технологических процессов длина секторов "плавает" в довольно широких пределах, то вместе с нею "плавает" и время, затраченное приводом, на их чтение — ведь линейная скорость вращения диска постоянна! Ну… практически постоянна. Лазерные диски, будучи по своей природе самосинхронизующимися устройствами, крайне неприхотливы к стабильности вращения привода. Главное, чтобы градиент изменения скорости был значительно ниже частоты следования питов и лендов (ведь, при пересечении границы каждого из них происходит автоподстройка генератора). Легко показать, что при постоянной угловой скорости вращения диска, его линейная скорость в процессе "раскрутки" спиральной дорожки неизбежно нарастает и, если не предпринять никаких мер, то на внешних секторах диска питы будут проноситься мимо лазерной головки с такой скоростью, что она попросту не успеет их считать.
Чтобы этого не произошло, в приводах CD- ROM используются специальные механизмы динамического регулирования, удерживающие линейную скорость вращения в заданных пределах. К тому же, конкретное значение линейной, равно как и угловой скорости вращения, нам не известно и его невозможно измерять с требуемой точностью. Но если скорость вращения диска неизвестна, то как прикажете определять длину сектора?
Давайте отталкиваться от того, что на коротком участке спиральной дорожки скорость вращения диска останется более или менее постоянной. Тогда, сравнивая точное время чтения соседних секторов, мы сможем приблизительно определить соотношение их длин. Действительно, если между двумя короткими секторами находится один длинный, то на графике возникнет резкий излом, изображающий собой "пик". Напротив, если длины трех или более секторов более или менее идентичны, то на графике образуется обширное "плато".
Результаты обследования одного из произвольно взятых дисков автора выглядят так (рис. 9.3). Эта "унылая изрытая горами местность", почему-то ассоциирующаяся с двуручной пилой, довольно капризна по своей природе. Повторный прогон программы с тем же самым диском никогда не даст полностью идентичный результат. Тем не менее, большинство пиков и впадин совпадают, и идентифицировать оригинальный диск все же возможно!
Рис. 9.3.унок 25 0x02F Узловые профили, полученные при двух прогонах одного и того же диска
А теперь давайте скопируем диск и попробуем сравнить его с оригиналом (рис. 9.4). Совсем другая картина! "Рельеф местности" до неузнаваемости изменился. Во-первых, вершины пиков категорически не совпадают. Во-вторых, у дубликата отмечается на удивление невысокое количество "впадин" (видать болванка хорошая попалась). И, наконец, в третьих, между 14 и 22 секторами, вместо огромного "трезубца", украшающего диск-оригинал, теперь раскинулась "протяжная равнина", "нахально" загибающаяся книзу.
Рис. 9.4.унок 26 0x030 Узловые профили двух различных дисков
Таким образом, измерение относительного времени чтения секторов, позволяет однозначно отличить оригинальный носитель от его копии. Причем, такое измерение легко осуществимо штатными средствами операционной системы и совместимо практически со всеми моделями приводов.
Защиты от пофайлового копирования диска (защиты уровня файловой системы)
Качественные и грамотно спроектированные защитные механизмы обычно содержат две независимых "линии обороны", работающих на секторном и на файловом уровнях соответственно. Защиты секторного уровня ориентированы на борьбу с посекторными копировщиками, копирующими весь диск целиком, то есть создающими его точную копию, а защиты файлового уровня, в свою очередь, предотвращают пофайловое копирование диска, равно как и выдирание с диска отельных файлов.
Может показаться, что при наличии качественной секторной защиты, файловая защита абсолютно бесполезна (тем более, что все файловые защиты элементарно обходятся даже штанными копировщикам, такими, например, как RoxioCD Copier). Однако это не так! Невозможность создания точной копии диска еще не означает невозможности "грабежа" хотя бы части его содержимого. Никто не спорит, что часть меньше целого, но все же это лучше чем совсем ничего. Хотите наглядный пример? Пожалуйста! Защитные механизмы мультимедийных энциклопедий "Наутилус Помпилиус —– Погружение" и "Агата Кристи —– Виртуальный Концерт[Y188] [n2k189] "
работают лишь на секторном уровне, но никак не препятствуют пофайловому копированию содержимого диска на винчестер или другой CD. Конечно, полученная "копия" оказывается неработоспособной и при запуске мультимедийной оболочки защита, "смачно ругнувшись" на пиратскую копию, аварийно завершает свою работу. Тем не менее, с обоими дисками можно работать и без оболочки, поскольку все альбомы/фотографии/видео-клипы хранятся в открытом, незашифрованном виде в формате WAV/BMP/AVI соответственно. Даже неквалифицированный пользователь без труда "выдерет" понравившиеся ему файлы с диска и… сможет делать с ними все, что захочет!
Отсюда мораль: не стоит путать защиту носителя (лазерного диска) с защитой его содержимого. Для защиты содержимого существует множество путей…
Защиты, препятствующие проигрыванию диска в PC CD-ROM
Бытовые проигрыватели аудиодисков—– намного менее интеллектуальные устройства, нежели компьютерные приводы CD-ROM. Подавляющее большинство из них не поддерживает многосессионные диски и довольно терпимо относится к искажению TOC'a, поскольку игнорирует добрую половину его полейсодержимого. Таким образом, внося строго дозированные искажения в TOC, можно добиться того, чтобы с точки зрения CD-плееров диск выглядел нормально, но был не читаем на компьютерных приводах CD-ROM.
Конкретные воплощения этой идеи выглядят довольно разнообразно. Вместо качественной музыки вам могут подсунуть специально созданную для компьютерного CD-ROM'a сессию данных, содержащую сильно сжатый MP3; могут скорректировать указатель на область Lead-outLead-Out область так, чтобы компьютерный CD-ROM прерывал воспроизведение через несколько секунд после начала проигрывания диска; могут исказить абсолютный стартовый адрес первого трека так, чтобы его LBA-адрес стал отрицательным… Во всех этих случаях нормальное использование диска на компьютерном приводе CD-ROM оказывается невозможным. Тем не менее, искаженный TOC защищенного диска может быть элементарно восстановлен (и чуть позже мы покажемстанет понятно как).
Защиты, базирующиеся на внесении неустранимых ошибок уровня C1/C2 —– в этом смысле гораздо хуже и для их взлома понадобиться привод, согласный не только обнаруживать ошибки, но и указывать на место их возникновения.