CRC, и как его восстановить

|

CRC and how to Reverse it Anarchriz/DREAD

Copyright (c) 1998,1999 by Anarchriz

Оригинал статьи можно найти по адресу:

http://huizen.dds.nl/-noway66/programming/crc.htm

 

Вступление

В этой статье дано краткое описание CRC и способов его восстановление. Мно­гие программисты и исследователи не знают деталей работы CRC, и практически никто не знает способов его восстановления, несмотря на то, что такие знания мог­ли бы им очень пригодиться. В первой части статьи приведено общее описание способов вычисления CRC, что может быть использовано для защиты данных или программ. Вторая часть рассказывает, каким образом можно восстановить CRC-32, что может быть использовано для взлома некоторых защит. По-видимому, эти ме­тоды используются теми, кто "исправляет" для Вас CRC, хотя я сомневаюсь, чтобы они рассказывали Вам, как это делается.

Сразу же хочу оговориться, что в статье используется довольно много матема­тических выкладок. Я считаю, что они понятны любому программисту среднего уровня. Зачем это нужно? Ну что ж, если Вы не знаете, зачем при обсуждении CRC необходима математика, то я предлагаю Вам поскорее воспользоваться кноп­кой "X" в правом верхнем углу экрана. Теперь, полагаю, все оставшиеся читатели имеют достаточные познания в области двоичной арифметики.

Часть 1. Путеводитель по CRC: что это такое, и как он вычисляется

Cyclic Redundancy Code (CRC) - циклический избыточный код

Все мы слышали о CRC. Если Вы забыли об этом, то Вам напомнят о нем непри­ятные сообщения от RAR, ZIP и других архиваторов, когда файл окажется повреж­ден в результате некачественного соединения или повреждения флоппи-диска. CRC — это значение, которое вычисляется для некоторого блока данных, напри­мер, для каждого файла во время архивации. При развертывании файла архиватор сравнивает это значение со вновь вычисленным CRC распакованного файла. Если они совпадают, то существует очень большая вероятность того, что этот новый файл получился идентичным исходному. При использовании CRC-32 вероятность пропустить изменение данных составляет всего 1/232.

Некоторые считают, что CRC означает Cyclic Redundancy Check (Циклическая избыточная проверка). Если бы это было так, то тогда в большинстве случаев этот термин использовался бы некорректно, ведь нельзя говорить, что "CRC равно 12345678" — как это проверка может чему-то равняться?. Кроме того, многие гово­рят, что программа имеет проверку по CRC — как это так, Циклическая избыточная проверка может иметь проверку? Напрашивается вывод: CRC является сокраще­нием выражения "Циклический избыточный код" и никак не "Циклическая избы­точная проверка".

Как делаются эти вычисления? Основная идея состоит в том, чтобы предста­вить файл, как одну огромную строку бит, и поделить ее на некоторое число; ос­тавшийся в результате остаток и есть CRC! У Вас всегда будет оставаться остаток (правда, иногда он может оказаться равным нулю), который частенько лишь на один бит меньше делителя — 9/3 = 3, остаток =0; (9 + 2)/3 = 3, остаток =2.

С битами деление выполняется иначе — оно представляет собой последова­тельное вычитание (X раз) из делимого некоторого числа (делителя), что в результа­те всегда оставит Вам некоторый остаток. Если Вы хотите восстановить исходное значение, то Вам необходимо умножить делитель на "X" (или сложить его само с собой "X" раз) и в конце добавить остаток.

Вычисление CRC использует особый вид вычитания и сложения, своего рода "новую арифметику". Компьютер "забывает" делать перенос при вычислении каж­дого бита.

Давайте взглянем на примеры: 1й — это обычное вычитание, а 2 и 3 — специ­альное:

clip_image002

В 1м примере во втором столбце справа производится вычисление 0-1=-1, сле­довательно, необходимо "занять" один бит из следующего разряда: (10+0)-1=1. (Это аналогично обычному десятичному вычитанию "на бумаге".) Особый случай — 1 + 1 в нормальных вычислениях дает в результате 10, и единица "переносится" в старший разряд. В специальных действиях (примеры 2 и 3) такой перенос "забывается". Аналогично, 0-1 должно бы равняться -1, что требует заему бита из старшего разряда (смотрите пример 1). Этот заем при вычислении также "забывают" сделать. Если Вы знакомы с программированием, то это должно Вам напомнить операцию "Исключающее ИЛИ" (exclusive OR, или более привычно — XOR), чем оно фактически и является.

Посмотрим теперь на деление:

Вот как оно делается в нормальной арифметике:

clip_image004

В CRC-арифметике все немного по-другому (пример 3):

clip_image006

Частное от деления совершенно не важно, и его нет необходимости запоминать, так как оно лишь на пару бит меньше той последовательности, для которой Вы хо­тите рассчитать CRC. Что действительно важно, так это остаток! Именно он может сообщить нечто достаточно важное обо всем файле. Фактически, это и есть CRC.

Обзор реального вычисления CRC

Для вычисления CRC нам необходимо выбрать делитель, который с этого мо­мента мы будет называть полиномом. Степень полинома (W — Width) — это номер позиции его старшего бита, следовательно, полином 1001 будет иметь степень "3", а не "4". Обратите внимание, что старший бит всегда должен быть равен 1, следова­тельно, после того, как Вы выбрали степень полинома, Вам необходимо подобрать лишь значения младших его битов.

Если Вы хотите вычислять CRC для последовательности бит, Вам следует убе­диться, что обработаны все биты. Поэтому в конце последовательности необходи­мо добавить W нулевых бит. Так для примера 3 мы может утверждать, что последо­вательность битов равнялась 1111. Взгляните на более сложный пример (пример 4):

Полином = 10011, степень W=4

Последовательность бит + W нулей = 110101101 + 0000

clip_image008

Здесь необходимо упомянуть 2 важных момента:

1. Операция XOR выполняется только в том случае, когда старший бит последо­вательности равен 1, в противном случае мы просто сдвигаем ее на один бит влево.

2. Операция XOR выполняется только с младшими W битами, так как старший бит всегда в результате дает 0.

Обзор табличного алгоритма

Понятно, что алгоритм, основанный на битовых вычислениях очень медленен и неэффективен. Было бы намного лучше проводить вычисления с целыми байтами. Но в этом случае мы сможем иметь дело лишь с полиномами, имеющими степень, кратную 8 (то есть величине байта). Давайте представим себе такие вычисления на основе полинома 32 степени (W = 32).

clip_image010

Здесь показан регистр, который используется для сохранения промежуточного результата вычисления CRC, и который я будут называть регистром CRC или про­сто регистром. Когда бит, выдвинутый из левой части регистра, равен 1, то выпол­няется операция "Исключающее ИЛИ" (XOR) содержимого регистра с младшими W битами полинома (их в данном случае 32). Фактически мы выполняет описанную выше операцию деления.

А что, если (как я уже предложил) сдвигать одновременно целые группы бит. Рассмотрим пример вычисления CRC длиной 8 бит со сдвигом 4 бит одновременно.

До сдвига регистр равен: 10110100

Затем 4 бита выдвигаются с левой стороны, тогда как справа вставляются новые 4 бита. В данном примере выдвигается группа 1011, а вдвигается — 1101. Таким образом мы имеем:

текущее содержимое 8 битного регистра (CRC) : 01001101

4 старших бита, только что выдвинутые слева : 1011

используемый полином (степень W = 8) : 101011100

Теперь вычислим новое значение регистра:

clip_image012

В старшей группе в позиции 0 у нас остался еще один бит, равный 1

clip_image014

Теперь все 4 бита старшей группы содержат нули, поэтому нам больше нет не­обходимости выполнять в ней операцию XOR.

То же самое значение регистра могло быть получено, если операнд сложить по XOR с операндом (*2). Это возможно в результате ассоциативного свойства этой операции — (a XOR b) XOR c = a XOR (b XOR c).

clip_image016

Если теперь применить к исходному содержимому регистра операцию XOR со значением (*3), то получим:

clip_image018

Видите? Тот же самый результат! Значение (*3) для нас достаточно важно, так как в случае, когда старшая группа бит равна 1011, младшие W = 8 бит всегда будут равны 10111100 (естественно, для данного примера). Это означает, что мы можем заранее рассчитать величины XOR-слагаемого для каждой комбинации старшей группы бит. Обратите внимание, что эта старшая группа в результате всегда пре­вратится в 0.

Вернемся снова к схеме 1. Для каждой комбинации битов старшей группы (8 битов), выдвигаемых из регистра, мы можем рассчитать слагаемое. Получим таблицу из 256 (или 28) двойных слов (32 бита) (такая таблица для CRC-32 приведена в приложении).

На мета-языке этот алгоритм можно записать следующим образом:

clip_image020

Прямой табличный алгоритм

Описанный выше алгоритм можно оптимизировать. Нет никакой необходимо­сти постоянно "проталкивать" байты строки через регистр. Мы можем непосред­ственно применять операцию XOR байтов, выдвинутых из регистра, с байтами об­рабатываемой строки. Результат будет указывать на позицию в таблице, значение из которой и будет в следующем шаге складываться в регистром.

Я не могу точно объяснить, почему такая последовательность действий дает тот же самый результат (он не следует из свойств операции "Исключающее ИЛИ"), од­нако, его большим преимуществом является отсутствие необходимости дополнять строку нулевыми байтами/битами (если Вы сможете найти этому объяснение, по­жалуйста, сообщите его мне :) ).

Давайте взглянем на схему алгоритма (схема 2):

clip_image022

"Зеркальный" прямой табличный алгоритм

Чтобы жизнь не казалась медом, была создана еще и так называемая "зеркальная" версия этого алгоритма. В "зеркальном" регистре все биты отражены относительно центра. Например, '0111011001' есть "отражение" значения '1001101110'.

Эта версия обязана своим возникновением существованию UART (микросхема последовательного ввода/вывода), которая посылает биты, начиная с наименее значимого (бит 0) и заканчивая самым старшим (бит 7), то есть в обратном порядке.

Вместо того, чтобы менять местами биты перед их обработкой, можно зеркаль­но отразить все остальные значения, участвующие в вычислениях, что в результате дает очень компактную реализацию программы. Так, при расчетах биты сдвигают­ся вправо, полином зеркально отражается относительно его центра, и, естественно, используется зеркальная таблица предвычисленных значений.

clip_image024

В этом случае наш алгоритм станет следующим:

1. Сдвигаем регистр на 1 байт вправо.

2. Выполняем операцию XOR только что выдвинутого вправо из регистра байта с байтом обрабатываемой строки для получения номера позиции в таблице ([0,255]).

3. Табличное значение складываем по XOR с содержимым регистра.

4. Если байты еще остались, то повторяем шаг 1.

Некоторые реализации алгоритма на ассемблере

Для начала приведен полный стандарт алгоритма CRC-32:

Название : "CRC-32"

Степень : 32

Полином : 04C11DB7

Начальное значение : FFFFFFFF

Отражение : Да

Последнее XOR с : FFFFFFFF

Для любознательных (в качестве приза) стандарт алгоритма CRC-16:

Название : "CRC-16"

Степень : 16

Полином :8005

Начальное значение : 0000

Отражение : Да

Последнее XOR с : 0000

Величина, обозначенная "Последнее XOR с", представляет собой то значение, которое складывается по XOR с содержимым регистра перед получением оконча­тельного результата CRC.

Кроме того, существуют "зеркальные" CRC-полиномы, однако, они выходят за рамки данной статьи. Если Вы хотите узнать о них по подробнее, обращайтесь с дополнительной литературе.

При написании примера на ассемблере я использовал 32-битый код в 16-битном режиме DOS (поэтому в примерах Вы обнаружите 16- и 32-битную смесь), что, од­нако, не сложно перевести в настоящий 32-битный код. Обратите внимание, что примеры на ассемблере полностью протестированы, примеры на языках Java и C являются их производными.

Итак. Вот реализация вычисления таблицы CRC-32:

clip_image026

no_topbit:

shr eax, 1 entrygoon:

inc cx

test cx, 8

jz entryLoop

mov dword ptr[ebx*4 + crctable], eax

inc bx

test bx, 256

jz InitTableLoop

Замечания — crctable - это массив из 256 значений, рассчитываемая нами таблица CRC;

— eax сдвигается вправо, так как в данном случает используется "зеркальный" алгоритм;

— по той же причине обрабатываются младшие 8 бит...

А вот реализация того же алгоритма на языке Java или C (int — 32-битное зна­чение):

for (int bx=0; bx<256; bx++){ int eax=0;

eax=eax&0xFFFFFF00+bx&0xFF; // инструкция 'mov al,bl' for (int cx=0; cx<8; cx++){ if (eax&&0x1) {

eax>>=1;

eaxA=poly;

}

else eax>>=1;

}

crctable[bx]=eax;

}

Реализация вычислений CRC-32 с помощью составленной таблицы:

computeLoop:

xor ebx, ebx

xor al, [si]

mov bl, al

shr eax, 8

xor eax, dword ptr[4*ebx+crctable]

inc si

loop computeLoop

xor eax, 0FFFFFFFFh

Замечания

— 'ds:si' указатель буфера, где находится обрабатываемая строка;

— cx количество обрабатываемых байтов (длина строки);

— eax содержит текущее значение CRC;

— таблица заранее вычисленных значений получена приведенной выше программой;

— начальное значение CRC в случае CRC-32 — FFFFFFFF;

— после окончания вычислений значение CRC складывается по XOR с величиной FFFFFFFF (аналог операции NOT).

На языках Java или C это будет выглядеть следующим образом:

for (int cx=0; cx>=8;' eaxA=crcTable[ebx];

}

eax/4 = 0xFFFFFFFF;

Таким образом, мы завершили первое знакомство с CRC. Если Вы хотите уз­нать о них немного больше, то прочтите другую мою статью, ссылку на которую можно найти в конце документа.

Хорошо! Теперь приступим с более интересной части: восстановлению CRC!

Часть 2. Восстановление CRC

Когда я размышлял об о способах восстановления CRCs Эта проблема возника­ла у меня неоднократно. Я пытался "обмануть" CRC, изобретая такую последова­тельность байтов, чтобы оказалось безразличным, какие символы и сколько их бу­дет стоять перед ней. Я не добился успехаs Затем я понял, что таким способом ус­пеха добиться невозможно, так как алгоритм CRC построен так, чтобы вне зависи­мости от того, какой бит Вы меняете, результат вычисления всегда (хорошо, почтш всегда) преображается совершенно неузнаваемо. Попробуйте сами поэкспери­ментировать с какой-либо простейшей CRC программой... :)

Я понял, что могу "корректировать" CRC лишь после изменения необходимых мне байтов. Я смог составить такую последовательность, которая преобразовывала CRC так, как мне это было необходимо! Рассмотрим конкретный пример. Имеется последовательность байтов:

clip_image028

Вам необходимо изменить байты, выделенные подчеркиванием (с 9 по 26). Кроме того, Вам понадобятся еще 4 байта (до 30й позиции) для построения такой последовательности, которая восстановит исходное значение CRC.

Когда Вы начинаете вычислять CRC-32 все идет прекрасно до байта в 9 позиции; в измененной же последовательности, начиная с этой точки, CRC меняется карди­нально. Даже после 26 позиции, байты после которой изменений не претерпели, получить исходного значения CRC оказывается невозможно.

Однако, прочитав статью до конца, Вы поймете, что следует предпринять в та­кой ситуации. В двух словах это можно описать следующим образом:

1. Необходимо вычислить значение CRC вплоть до 9 позиции и запомнить его.

2. Продолжить расчет CRC до 30 позиции включительно (байты, которые Вы со­бираетесь менять, плюс 4 байта дополнительно) и также запомнить значение.

3. Рассчитать значение CRC для "новых" байтов, включая 4 дополнительных (то есть всего для 27-9+4=22 байтов), используя в расчетах величину, получен­ную в шаге 1, снова его запомнить.

4. Теперь мы имеем "новое" значение CRC. Однако, нам необходимо, чтобы CRC имело "старое" значение. Для расчета 4 дополнительных байтов необхо­димо "обратить" алгоритм.

До настоящего момента мы могли выполнить только этапы с первого по третий. Теперь поучимся делать четвертый.

Восстановление CRC-16

Для начала мы займемся восстановлением 16-разрядного CRC. Итак, мы сделали в последовательности все необходимые изменения, и теперь нам необходимо вер­нуть прежнее значение CRC. Мы знаем старое значение CRC (рассчитанное до изменения последовательности), а также его новое значение. Нам необходимо со­ставить такую 2-байтную последовательность, которая позволит изменить текущее значение CRC в его прежнее состояние.

Сначала мы рассчитаем CRC с учетом 2 неизвестных байт (назовем их "X" и "Y"). Предположим, что регистр имеет значения a1 и a0, а вдвигаемый байт равен 00. Взглянем еще раз на схему 3 и приступим.

Возьмем последовательность "X Y". Байты обрабатываются слева-направо. Пред­положим, регистр равен "a1 a0". Будем обозначать операцию XOR символом "+".

clip_image030

Теперь давайте переварим полученные результаты :). (Не отчаивайтесь, пример с реальными числами не за горами).

Подумайте! Мы хотим получить в регистре значение d1 d0 (исходное CRC), зная содержимое регистра обработки измененной последовательности символов (значение a1 a0). Вопрос — какие 2 байта, или, другими словами, какие значения "X" и "Y" нам необходимо использовать при вычислении CRC?

Начнем рассуждать с конца. d0 должно быть равно Ы+е0, а d1 = c1. Однако, я так и слышу Ваш возглас: "Как я могу узнать значения Ь1 и c0?!!!". Но нужно ли мне напоминать Вам о Таблице? А значение c0 можно получить из слова c0 c1, ведь мы знаем значение c1. Следовательно нам потребуется процедура поиска. А, если Вы нашли это слово, то уж постарайтесь запомнить и его индекс, с помощью которого мы сможем найти старшие группы байт, то есть величины (1) и (2).

Но как же нам получить b1 Ь0, несмотря на то, что мы нашли c1 c0? Если M+c0=d0, то b1=d0+c0! Ну, а затем, найдя Ь1, применим процедуру поиска для получения слова b1 b0 . Теперь у нас есть все для вычисления значений "X" и "Y"! Превосходно, не правда ли?!!!

clip_image034

Численный пример восстановления CRC-16

Давайте теперь рассмотрим реальный числовой пример:

• исходное значение регистра: (a1=)DEh (a0=)ADh

• нужно получить: (d1=)12h (d0=)34h

Найдем в таблице для CRC-16 (см. Приложение) строку, начинающуюся с 12h. Это позиция 38h, которая содержит значение 12C0h. Это единственное такое зна­чение, ведь, вспомните, мы рассчитывали каждую позицию в таблице для всех воз­можных величин старшей группы, всего их было 256.

Теперь мы знаем, что (2)=38, c1=12, а c0=C0, следовательно b1=C0+34=F4, и следующим шагом необходимо найти позицию, значение в которой начинается с F4h. Это оказалась позиция 4Fh содержащая F441h. Получаем, (1)=4F, b1=F4, b0=41. Теперь у нас есть все, что рассчитать "X" и "Y":

clip_image036

Следовательно, чтобы изменить значение регистра CRC-16 со значения DEAD на значение 1234 необходимы байты E2 A7 (именно в таком порядке).

Как Вы видите, чтобы восстановить CRC, Вы должны "просчитать" его в обратном порядке и запомнить все полученные значения. При реализации программы про­смотра таблицы обратите внимание, что для процессоров фирмы Intel характерно за­поминание байтов в обратном порядке (сначала младший, а затем старший байты).

Теперь, когда Вы, надеюсь, поняли, как восстанавливать CRC-16, займемся CRC-32.

Восстановление CRC-32

CRC-32 восстанавливается столь же просто, как и CRC-16. Правда, нам теперь придется иметь дело с 4 байтами вместо 2. Итак, смотрите и сравнивайте.

Допустим, что мы имеем 4 байтовую строку "X Y Z W", байты берутся с левой стороны.

Предположим, что в регистре содержится значением "a3 a2 a1 a0", при этом "a3" является старшим, а "a0" — младшим (наименее значащим) байтом.

clip_image038

Обработаем третий байт ("Z"):

(a2+b1+c0)+Z рассчитанная старшая группа (3)

d3 d2 d1 d0 найденная для нее табличная последовательность

00 c3 b3+c2 a3+b2+c1 сдвиг регистра вправо

00+d3 c3+d2 b3+c2+d1 a3+b2+c1+d0 сложение двух предыдущих строк по XOR

Новое содержимое регистра: (d3) (c3+d2) (b3+c2+d1) (a3+b2+c1+d0)

Обработаем четвертый байт ("W"):

(a3+b2+c1+d0) +W рассчитанная старшая группа (4)

e3 e2 e1 e0 найденная для нее табличная последовательность

00 d3 c3+d2 b3+c2+d1 сдвиг регистра вправо

00+e3 d3+e2 c3+d2+e1 b3+c2+d1+e0 сложение двух предыдущих строк по XOR

Новое содержимое регистра: (e3) (d3+e2) (c3+d2+e1) (b3+c2+d1+e0)

Теперь я перепишу это в несколько ином виде:

clip_image040

Короче, вся последовательность действий аналогична 16-битной версии.

Теперь я приведу пример с реальными значениями. Таблица для CRC-32 дана в Приложении.

Предположим, что начальное значение CRC-регистра (a3 a2 a1 a0) равно "AB CD EF 66".

Нам необходимо получить величину (f3 f2 f1 f0) "56 33 14 78". Итак:

clip_image042

Вывод: чтобы изменить значение CRC-32 с "ABCDEF66" на "56331478" необхо­дима последовательность байтов "B8 C4 53 8E".

Алгоритм восстановления CRC-32

Если взглянуть на ручные расчеты необходимой последовательности байтов для изменения значения CRC с "a3 a2 a1 a0" на "f3 f2 f1 f0", то их перевод в простой и компактный алгоритм покажется достаточно сложным.

Рассмотрим еще раз обобщенную версию окончательных расчетов:

clip_image044

Это очень похоже на схему 4, добавлены лишь некоторые значения, что, однако, поможет нам составить понятный алгоритм. Возьмем буфер длиной 8 байт, по одно­му байту на каждую строку схемы 5. Заполним байты с 0 по 3 значениями a0-a3, а байты с 4 по 7 — f0-f3. Как и прежде, возьмем значение e3, которое равно f3, и по­ищем полную последовательность в таблице CRC, а затем выполним сложение этого значения по XOR с регистром, начиная с позиции 4 (как на схеме 5). Автоматически получаем значение d3 — мы выполнили "f3 f2 f1 f0" XOR "e3 e2 e1 e0", а

f2+e2=d3. Так как мы знаем, чему равно значение (4) (это номер позиции для e3 e2 e1 e0), то сложим его по XOR со значением байта 3. Зная значение d3, по­ищем полное значение d3 d2 d1 d0 , и повторим сложение по XOR, но на одну по­зицию правее, то есть с позиции 3 (следите по схеме!). Номер позиции для d3 d2 d1 d0 (значение (3)) складывается со значением в позиции 2. В позиции 5 по­лучаем величину c3, так как "f1+e1+d2=c3".

Продолжим работу до операции XOR последовательности b3 b2 b1 b0 в пози­ции 1. Все! Теперь байты 0-3 буфера содержат искомые значение X-W!

Обобщим только что описанный алгоритм:

1. В 8-байтном буфере заполним позиции 0-3 значениями a0-a3 (начальные значения байтов CRC), а позиции 4-7 значениями f0-f3 (байты CRC, которые необходимо получить в результате).

2. Возьмем значение из позиции 7 и используем его для поиска в таблице полно­го значения.

3. Сложим его (dword) по XOR с содержимым буфера, начиная с позиции 4.

4. Номер позиции в таблице, в которой содержалось обнаруженное значение, сложим по XOR с содержимым позиции 3.

5. Повторим шаги 2-4, каждый раз уменьшая номер позиции в буфере на 1.

Реализация алгоритма восстановления

Теперь самое время обсудить реализацию алгоритма. Ниже приведен код алго­ритма восстановления CRC-32 на языке ассемблера (несложно модифицировать его для других языков программирования или иных стандартов расчета CRC). Об­ратите внимание, что для процессоров фирмы Intel запись и чтение двойных слов происходит в обратном порядке.

clip_image046

Замечание: Используются регистры eax, di и bx. Реализация процедуры GetTableEntry

clip_image048

На выходе из процедуры регистр eax будет иметь полное содержимое из пози­ции в таблице, а регистр bx — номер этой позиции.

Заключение

Ну вот мы и добрались до конца этой статьи. Если Вам кажется, что теперь все эти программы, защищенные с помощью CRC должны "поднять лапки к верху", то должен Вас разочаровать. Очень легко сделать анти-анти-CRC код. Для правильно­го восстановления CRC требуется точно знать, какая часть кода проверяется по CRC, и какой алгоритм при этом используется. Простейшей мерой защиты будет использование 2 различных методов расчета, или комбинирование его с другой системой защиты.

Тем не менееэ Я надеюсь, что чтение этого бреда было для Вас интересным, и что Вы получили от этого такое же удовольствие, каким для меня было удовольст­вие его написать.

Выражаю большую благодарность бета-тестерам Douby/DREAD и Knotty Dread за полезные комментарии, которые позволили улучшить эту статью.

Пример программы коррекции CRC-32 можно найти на моем сайте http://surf.to/anarchriz -> Programming -> Projects (она, правда, все еще находится в состоянии бета-версии, однако, общие идеи в ней почерпнуть Вы все-таки сможете).

Дополнительную информацию по группе DREAD можно найти на сайте http://dread99.cjb.net.

Если у Вас все еще останутся вопросы, то Вы можете связаться со мной по элек­тронной почте anarchriz@hotmail.com, или на каналах EFnet (IRC) #DreaD, #Win32asm, #C.I.A и #Cracking4Newbies (проверяйте их именно в этой последо­вательности).

CYA ALL! - Anarchriz

"The system makes its morons, then despises them for their ineptitude, and rewards its 'gifted few' for their rarity." - Colin Ward

("Система всегда плодит неугодных себе, презирает их за их безрассудство, и вознаграждает одареннейших из них за их исключительность." — Колин Уорд.)

Предлагаю ознакомиться с аналогичными статьями: