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 — специальное:
В 1м примере во втором столбце справа производится вычисление 0-1=-1, следовательно, необходимо "занять" один бит из следующего разряда: (10+0)-1=1. (Это аналогично обычному десятичному вычитанию "на бумаге".) Особый случай — 1 + 1 в нормальных вычислениях дает в результате 10, и единица "переносится" в старший разряд. В специальных действиях (примеры 2 и 3) такой перенос "забывается". Аналогично, 0-1 должно бы равняться -1, что требует заему бита из старшего разряда (смотрите пример 1). Этот заем при вычислении также "забывают" сделать. Если Вы знакомы с программированием, то это должно Вам напомнить операцию "Исключающее ИЛИ" (exclusive OR, или более привычно — XOR), чем оно фактически и является.
Посмотрим теперь на деление:
Вот как оно делается в нормальной арифметике:
В CRC-арифметике все немного по-другому (пример 3):
Частное от деления совершенно не важно, и его нет необходимости запоминать, так как оно лишь на пару бит меньше той последовательности, для которой Вы хотите рассчитать CRC. Что действительно важно, так это остаток! Именно он может сообщить нечто достаточно важное обо всем файле. Фактически, это и есть CRC.
Обзор реального вычисления CRC
Для вычисления CRC нам необходимо выбрать делитель, который с этого момента мы будет называть полиномом. Степень полинома (W — Width) — это номер позиции его старшего бита, следовательно, полином 1001 будет иметь степень "3", а не "4". Обратите внимание, что старший бит всегда должен быть равен 1, следовательно, после того, как Вы выбрали степень полинома, Вам необходимо подобрать лишь значения младших его битов.
Если Вы хотите вычислять CRC для последовательности бит, Вам следует убедиться, что обработаны все биты. Поэтому в конце последовательности необходимо добавить W нулевых бит. Так для примера 3 мы может утверждать, что последовательность битов равнялась 1111. Взгляните на более сложный пример (пример 4):
Полином = 10011, степень W=4
Последовательность бит + W нулей = 110101101 + 0000
Здесь необходимо упомянуть 2 важных момента:
1. Операция XOR выполняется только в том случае, когда старший бит последовательности равен 1, в противном случае мы просто сдвигаем ее на один бит влево.
2. Операция XOR выполняется только с младшими W битами, так как старший бит всегда в результате дает 0.
Обзор табличного алгоритма
Понятно, что алгоритм, основанный на битовых вычислениях очень медленен и неэффективен. Было бы намного лучше проводить вычисления с целыми байтами. Но в этом случае мы сможем иметь дело лишь с полиномами, имеющими степень, кратную 8 (то есть величине байта). Давайте представим себе такие вычисления на основе полинома 32 степени (W = 32).
Здесь показан регистр, который используется для сохранения промежуточного результата вычисления CRC, и который я будут называть регистром CRC или просто регистром. Когда бит, выдвинутый из левой части регистра, равен 1, то выполняется операция "Исключающее ИЛИ" (XOR) содержимого регистра с младшими W битами полинома (их в данном случае 32). Фактически мы выполняет описанную выше операцию деления.
А что, если (как я уже предложил) сдвигать одновременно целые группы бит. Рассмотрим пример вычисления CRC длиной 8 бит со сдвигом 4 бит одновременно.
До сдвига регистр равен: 10110100
Затем 4 бита выдвигаются с левой стороны, тогда как справа вставляются новые 4 бита. В данном примере выдвигается группа 1011, а вдвигается — 1101. Таким образом мы имеем:
текущее содержимое 8 битного регистра (CRC) : 01001101
4 старших бита, только что выдвинутые слева : 1011
используемый полином (степень W = 8) : 101011100
Теперь вычислим новое значение регистра:
В старшей группе в позиции 0 у нас остался еще один бит, равный 1
Теперь все 4 бита старшей группы содержат нули, поэтому нам больше нет необходимости выполнять в ней операцию XOR.
То же самое значение регистра могло быть получено, если операнд сложить по XOR с операндом (*2). Это возможно в результате ассоциативного свойства этой операции — (a XOR b) XOR c = a XOR (b XOR c).
Если теперь применить к исходному содержимому регистра операцию XOR со значением (*3), то получим:
Видите? Тот же самый результат! Значение (*3) для нас достаточно важно, так как в случае, когда старшая группа бит равна 1011, младшие W = 8 бит всегда будут равны 10111100 (естественно, для данного примера). Это означает, что мы можем заранее рассчитать величины XOR-слагаемого для каждой комбинации старшей группы бит. Обратите внимание, что эта старшая группа в результате всегда превратится в 0.
Вернемся снова к схеме 1. Для каждой комбинации битов старшей группы (8 битов), выдвигаемых из регистра, мы можем рассчитать слагаемое. Получим таблицу из 256 (или 28) двойных слов (32 бита) (такая таблица для CRC-32 приведена в приложении).
На мета-языке этот алгоритм можно записать следующим образом:
Прямой табличный алгоритм
Описанный выше алгоритм можно оптимизировать. Нет никакой необходимости постоянно "проталкивать" байты строки через регистр. Мы можем непосредственно применять операцию XOR байтов, выдвинутых из регистра, с байтами обрабатываемой строки. Результат будет указывать на позицию в таблице, значение из которой и будет в следующем шаге складываться в регистром.
Я не могу точно объяснить, почему такая последовательность действий дает тот же самый результат (он не следует из свойств операции "Исключающее ИЛИ"), однако, его большим преимуществом является отсутствие необходимости дополнять строку нулевыми байтами/битами (если Вы сможете найти этому объяснение, пожалуйста, сообщите его мне :) ).
Давайте взглянем на схему алгоритма (схема 2):
"Зеркальный" прямой табличный алгоритм
Чтобы жизнь не казалась медом, была создана еще и так называемая "зеркальная" версия этого алгоритма. В "зеркальном" регистре все биты отражены относительно центра. Например, '0111011001' есть "отражение" значения '1001101110'.
Эта версия обязана своим возникновением существованию UART (микросхема последовательного ввода/вывода), которая посылает биты, начиная с наименее значимого (бит 0) и заканчивая самым старшим (бит 7), то есть в обратном порядке.
Вместо того, чтобы менять местами биты перед их обработкой, можно зеркально отразить все остальные значения, участвующие в вычислениях, что в результате дает очень компактную реализацию программы. Так, при расчетах биты сдвигаются вправо, полином зеркально отражается относительно его центра, и, естественно, используется зеркальная таблица предвычисленных значений.
В этом случае наш алгоритм станет следующим:
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:
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 так, как мне это было необходимо! Рассмотрим конкретный пример. Имеется последовательность байтов:
Вам необходимо изменить байты, выделенные подчеркиванием (с 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 символом "+".
Теперь давайте переварим полученные результаты :). (Не отчаивайтесь, пример с реальными числами не за горами).
Подумайте! Мы хотим получить в регистре значение 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"! Превосходно, не правда ли?!!!
Численный пример восстановления 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":
Следовательно, чтобы изменить значение регистра 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" — младшим (наименее значащим) байтом.
Обработаем третий байт ("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)
Теперь я перепишу это в несколько ином виде:
Короче, вся последовательность действий аналогична 16-битной версии.
Теперь я приведу пример с реальными значениями. Таблица для CRC-32 дана в Приложении.
Предположим, что начальное значение CRC-регистра (a3 a2 a1 a0) равно "AB CD EF 66".
Нам необходимо получить величину (f3 f2 f1 f0) "56 33 14 78". Итак:
Вывод: чтобы изменить значение CRC-32 с "ABCDEF66" на "56331478" необходима последовательность байтов "B8 C4 53 8E".
Алгоритм восстановления CRC-32
Если взглянуть на ручные расчеты необходимой последовательности байтов для изменения значения CRC с "a3 a2 a1 a0" на "f3 f2 f1 f0", то их перевод в простой и компактный алгоритм покажется достаточно сложным.
Рассмотрим еще раз обобщенную версию окончательных расчетов:
Это очень похоже на схему 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 запись и чтение двойных слов происходит в обратном порядке.
Замечание: Используются регистры eax, di и bx. Реализация процедуры GetTableEntry
На выходе из процедуры регистр 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
("Система всегда плодит неугодных себе, презирает их за их безрассудство, и вознаграждает одареннейших из них за их исключительность." — Колин Уорд.)
0 коммент.:
Отправить комментарий