▄▓▄▄ ▄ ▄ ▄ ▄ ▄▄▄▓▀█▀▀█▀▀█▀▄▄▄ ▄ ▐▌▐▌▐▌▐▐▌▐ ▄ ▄▀▓▓▀▓▄▀ ▄▀ ▄▀ ▄▀ ▄▀▄▄ ▄ ▄▀ ▐▄■█▄██▄▌█▄▌▐▌ ▄▀ ▄▀▓▀▄ ░▐▌ ▐▌ ▄▀ ▐▌ █ ▐▌▄▀▄ ▐▌ █▄▀▄█▀▄■▀▄▀█▄▀█ ▐▌ █▓▄▀▐▌░ █ █ ▐▌ █ ▐▌▓ ▓ ▄▐▌ █▀▐▌▄▐▄ ▓▀ ▄▌▄▐▌▀▄ ▐▓▀████▄ ▄▓▄█ ▄▌▄██▓▄▓▄████▀█ ▄▄▄▄▄▄▄▄▄▄▄▄▄▄ ▄▓▀■█▀█▀ ▀▓▄▄▓▀ ▀█▀█■▀▓▄ █▀▓▓▀▄▐ ■▀ ▄▀▓▄█▓▀█▀▀■ ▌▄▀█▓█▌ ████▀▀█████▀▀█▐▓▌▓▐█▓▌ ▀ █▓ ▀ ▐▓█▌▓▐▓▌ ▄▓▀▄▌████▓▄▄ ▀▀█▀ ▄▄▓████▄▄▀▓▄ ███▄▀▓▄▀█▀▄▓ █ ▀▓▄▄■▀▄ ▄▒▀█▒▄ ▄▀■▄▄▓▀▄▐▌▄ ▄▓▀ ▀▄▀▓▄▄▓▀ ▀▀ ▄▀▓▄▀■▐▌ █▀▄▓▀▐█▌▄▓▀ ▄▓▀▄▄▐▌■▀▄▓█▀▀▄▄▀▀█▓▄▀■▐▌▄██▄▀■▐▓▌ █ ■ ▐▓▌ █ ■▐▓▌▄▀ ▌▐█▌░ ███▌▒▐█▌███▄▀▄▐▓▀ ▄▀█▀█▄ ▀▓▌▄▀ ██████▄▀▓▄▄ ▀▄████▄ ▀▄▓▀ █▄▀▓▄▐▀ ▀▓▄ ▀▓▄▀███▄ ▀▄█▄▄ ▄ ▄█▄▀▄▄▄████████▓▄▀▓▒█▄▓█▀▄▄▄▄▀█▓▄█▒▀▀ ███▄▄▓▀█▄ ▄██▄ ▄█████▄▄ ▀▀█▀▀ ▄▄▄▄ ▀▀ ▄▄ ▀ ▄▄▀▀▄▀▀█▄▄█▀ ▀█▄▄█▀ ███▓▀▄▓▄▀█▀▄▄▀█▀▄▄▀▄▄ ▀██▄ ▄██▀▀▄▄▀██▀▄▄▀█▀▄▀██▄ ▄▄▄▓██████▓▄▄▄█▀█▄ █▓▀▄████▓▄▓███▄█▀▄████▓▄▀▓██▀▄██▓█▓ ▄▓███▄███▄▀███▀▄▄▄▄▄▀█▄██▀▄▄█▄▀█▄ █ ███▓▀ ██▌ ▀▓█▓▀██▌ ▀▀█▓▄▀▄██▓▀ █▌███▀▓█▓▀██▌█▌▐██▀▀▀▓█ █▀▄█████▓▄▀█ █▄▀▓▀▄█▌▐██ ▀ ▐██ ▐██▓▀▓▀ ▄█▐██▓ ▀ ▓█▌ ██ ██▓▀ ▀███▌█ ███▄▓█▄█▄▐▓▒ ▐██ ▄███▓▀ ▄▄█▐█▌██▌ ▐█▓ ██▌coax!cph█▌ █▓▐█ █▄█ █▓█▐█▌▓▌ ▓██▌▓▀▀ ▄███▓▀ █▌██ ▐██ ▀██▓▄▄ ▀▀ ▐█▌██ █▄██▄█▄█▄▐█▌ ▐██▀ ▄█▄▐██▌ ▐█▐██ ██▌ ▀██▓▄ ▀▀██▓▐█ █▄█▌█▐▀█▀▐█▓ ██▌ ▄██▓▀ ▀██▄▄ ██▐██▌ ▐██▌▄█▓ ▐██ ▄█▄ ██▌█ █▄█▄▄▄█▄▌█▓▌ ▀████▓▀ ▀██▓█▓▌██▓ ██▓ ███▌ ▄▄█▓▀▐████▄ ▐█▓▐█ █▄█▄▀▄▀▄▀▄▀▄▄▄▄▄▄▄▄▀▀▀▄▄▄▄▄▄▄▄▄▄▀▀▄▄▄▀▄▄▄▄▄▄▄▀▄▄▄▀████▓▀▄▄▄▀█████▓▀▄▐█ █▄█▄▓▄▓▄▓TEAM-53.TUTORiALS.PACK.NUMBER.SiXTEEN.#16 ▀▀▄▄█▄█▄█▄▀█▀▓ █▀██ ▀▀▀▀█▀█▀█▀█▀█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█▀█▀█▀█▀█▀▀▀▀ "Почему защита CloneCD была сломана" Автор: jB Перевод: lord_Phoenix Предисловие Эта статья написана не для того, чтобы рассказать вам, как сломать CloneCD, а для того, чтобы показать что нужно учесть, если используешь криптографию в своих программах. И вот - защита CloneCD сломана. Эта программа долгое время была "черным ящиком" для кейгенеров. Криптография все чаще и чаще используется при защите программ, но еще чаще используется неправильно. В этой программе используется сильный, практически неломаемый алгоритм, основанный на очень даже сложной математике. CloneCD - одна из первых программ, в которой, для проверки рег. информации, используется криптография еллиптических кривых(дальше - ECC). Microsoft тоже отличилась, в Win98, использовала 2 алгоритма с еллиптическими кривыми на поле GF(p), 241 бит. На то время, наибольшим когда-либо рассчитанным дискретным логарифмом был 97 битовый. В 2002 году, Крис Монико (Chris Monico) со своей командой высчитали дискретный логарифм на GF(p) - 109 бит. Для этого потребовалось 10000 компьютеров и 549 дней. Сегодня, логарифм такого размера может быть вычислен, без особых трудностей. CloneCD, Resotrator и WinRAR, насколько я помню, первые программы, в которых использовано ECC - GF(2^m). Во всех трех, все основано на исходнике Pegwit, альтернативе PGP, разработанной Джорджем Барвудом (George Barwood). И все эти три программы были сломаны Dimedrol'ом. Сначала, я расскажу про проверку рег. информации, в общем. Далее, покажу ошибку разработчиков. И наконец - кодинг кейгена! 1. Анализ алгоритма CloneCD запротекчена ASProtect'ом. Тут я использовал Stripper 2.07. Дамп вышел нерабочий, но для анализа в IDA его хватит. Все функции, связанные с криптографией находятся в библиотеке ElbyCrypt.dll 1.1 Формат рег. информации - имя/код После краткого анализа дампа в IDA, я быстро нашел процедуру проверки рег. кода: .text:00419708 push 50h ; int len_max .text:0041970A lea eax, [ebp-16Ch] .text:00419710 push eax ; char *src .text:00419711 lea edx, [ebp-1Ch] .text:00419717 push edx ; char *dest .text:00419718 call ElbyCrypt.CookText .text:0041971D add esp, OCh .text:00419720 lea ecx, [ebp-1Ch] .text:00419726 push ecx .text:00419727 call _strlen .text:0041972C pop ecx .text:0041972D сmp eax, 3 .text:00419730 jnb short length_ok Введенное имя модифицируется в процедуре ElbyCrypt_CookText. Имя переводится в нижний регистр и убираются пробелы. Длинна имени должна быть больше или равняться трем. .text:004l97AE push edx ; char *key_out .text:004197AF lea eax, [ebp-OCCh] .text:004197B5 push eax ; char *key_in .text:004l97B6 call CookKey .text:004l97BB add esp, OCh .text:004l97BE lea ecx, [ebp-OCCh] .text:C04l97C4 push ecx ; char *key .text:004l97C5 lea eax, [ebp-lB8h] .text:004l97CB push eax ; cpPair sig .text:004197CC call AssemmbleKeys .text:004l97Dl add esp, 8 .text:004197D4 test eax, eax .text:004197D6 jz short wrong_key_format .text:004197D8 push 0 .text:004197DA lea edx, [ebp-lB8h] .text:004197EO push edx ; cpPair sig .text:004197El lea ecx, [ebp-16Ch] .text:004197E7 push ecx ; char *name .text:004197E8 call VerifyKey .text:004197ED add esp, OCh .text:004197F0 mov ebx, eax .text:004197F2 jmp short loc_4197F6 Рег. код модифицируется в процедуре CookKey - переводится в нижний регистр, проверяется формат - в рег.коде могут быть только hex-символы(т.е. 0-9 и a-f). Процедура AssembleKeys формирует подпись (r, s), из 2 больших чисел - из рег. кода. Первый байт показывает количество word'ов из 16 бит, которые собссно и составляют большое число. Затем идут эти word'ы, потом другой байт - количетсво word'ов следующего большого числа, и опять собссно word'ы. Вот, пример рег. кода (скарженный ключ, имя не пишу, чтобы вы не могли его использовать ;p ): 10F6101C94A78AFOB2AE709395408175C6983C7B51E31855452 82C92AA088FBEB10F3D9328ED23E7E44A04CA5F4B9D067CC8AB 69F2E2B4BFB6710252D1011BBF Рег. код интерпретируется так: - 10h word'ов - из первой части рег. кода: F6101C94A78AFOB2AE709395408175C6983C7B51E3185545282C92AA088FBEB1 (первая часть подписи) - 0Fh word'ов из второй части рег.кода: 3D9328ED23E7E44A04CA5F4B9D067CC8AB69F2E2B4BFB6710252D1011BBF (вторая часть подписи) Назовем первое большое число S,а второе - R. Большие числа - в виде word'ов по 16 бит, справа на лево. Подпись теперь такова, после переворота строк: / R - 1BBFD1010252B671B4BFF2E2AB697CC89D065F4B04CAE44A23E728ED3D93 \ S = B1BE8F08AA922C28455518E3517B3C98C6758140959370AEB2F08AA7941C10F6 Лицензия состоит из имени(более трех символов, без пробелов, в нижнем регистре) и рег.кода из 2 больших чисел, в виде описанном выше. 1.2 Алгоритм проверки Если имя достаточно длинное(>=3) и формат рег.кода верный, то лицензия проверяется в процедуре VerifyKey. Ей передаются - имя, лицензия(здесь 0), но это нас не интересует. Я советую вам сначала прочитать документацию Pegwit'а, так как "сердце" процедуры проверки базируется на его процедурах. VerifyKey proc near ; CODE XREF: sub_417394+17F ; .text:0O4197E8 ... hash_buffer = dword ptr -30h var_22 = dword ptr -22h var_lE = dword ptr -1Eh var_lA = dword ptr -1Ah var_18 = dword ptr -18h crc = dword ptr -4 name = dword ptr 8 sig = dword ptr 0Ch arg_8 = dword ptr 10h push ebp mov ebp, esp add esp, 0FFFFFF80h push ebx push esi push edi mov esi, [ebp+sig] mov [ebp+crc], OFFFFFFFFh mov bl, 1 call ElbyCrypt_Init test eax, eax jz short loc_417B61 xor eax, eax jmp loc_417D8B ;------------------------------------------------------------------------------------ loc_417B61: ; CODE XREF: VerifyKey+lC push 26h ; n push 0 ; c lea edx, [ebp+hash_buffer] push edx ; hash_buffer call _memset add esp, OCh mov ecx, [ebp+name] push ecx ; char *str call CalcUserCRC pop ecx mov edi, eax push esi ; cpPair *sig lea eax, [ebp+hash_buffer] push eax ; vlPoint vlMac push offset vlPublicKeyl ; vlPoint vlPublicKey call ElbyCrypt.DecodePublic add esp, OCh push 18h ; len lea edx, [ebp+crc] push edx ; unsigned int *crc lea ecx, [ebp+hash_buffer] push ecx ; unsigned char *data call DoCRCData add esp, OCh mov eax, [ebp+hash_buffer+18h] mov edx, [ebp+crc] cmp eax, edx jz short crc1_ok [...] call ElbyCrypt.Quit xor eax, eax jmp wrong_serial Приведенный код неполный. Часть кода Elby-Crypt_DecodePublic/DoCRCData в программе повторяется 3 раза, с разными публичными ключами. Нам интересен только первый. Я не знаю для чего нужны другие 2 ключа, и я не смог найти лицензию, сделанную с помощью них. Процедура VerifyKey вызывает ElbyCrypt_Init, которая ускоряет вычисления GF(2^m) (смотрите [2] для доп.информации). Реально же, это хорошо известная функция Pegwit - gfInit. Пустой буфер предназначен для большого числа - хеша, полученнго с помощью Elby_CryptDecodePublic. Потом, от имени берется хеш, с помощью CalcUserCRC. Эта процедура вызывает CookText для конрветирования в нижний регистр и "очистки" от пробелов; тут вычисляется 32 битный хеш, названный CRC8(не знаю стандартный ли это хеш) с помощью процедуры DoCRCString. Все эти процедуры есть в криптографической библиотеке Elby. Я узнал их имена, посмотрев на имена экспортируемых процедур библиотеки. В ElbyCrypt_DecodePublic передается 3 значения-аргумента: - публичный ключ, который является точкой на кривой системы. Публичный ключ - "сжатая" точка на кривой(прим.пер.-гуглим "elliptic curve decompressing", или читаем доки миракла). Так как на еллиптической кривой только 2 точки могут иметь одинаковые X-координаты, такие точки можно различить по значущему биту(0 или 1) (прим.пер. - lsb - least significant bit ;), добавляемому к ним. Так что публичный ключ является большим числом. - vlMac, большое число, мессадж для проверки(сначала - пустой буффер) - подпись из двух больших чисел (R, S), из рег. кода Эта процедура вычисляет большое число - мессадж, и копирует его в vlMac, вы видем, что используется алгоритм Nyberg-Rueppel. В данный момент, у нас есть достаточно информации, чтобы найти подписанный мессадж. Тоесть есть возможность сгенерировать vlMac. От первых 24(0x18) символов vlMac получается хеш, с помощью DoCRCData. Первая проверка рег.кода - сравнение полученного хеша, с 4 последющими байтами. Если первая проверка пройдена - идет вторая проверка, где уже участвует введенное имя: crc1_ok: ; CODE XREF: VerifyKey+6D ; VerifyKey+A4 ... call ElbyCrypt.Quit mov ecx, dword_4DC708 xor edx, edx mov dl, bl mov [ecx+280hH, edx mov eax, [ebp+hash_buffer+12h] cmp edi, eax jz short crc2_ok xor eax, eax jmp wrong_serial 4 байта vlMac, начиная с 18(0x12) символа сравниваются с хешем от имени, вычисленным раньше (смотрите выше, указатель на хеш - в edi). Это и есть вторая проверка. Если и она пройдена - лицензия валидная! Таким образом, для проверка валидности лицензии используются 64 бита - 32 бита для проверки vlMac и еще 32 - для проверки соотвествия рег.кода имени. Исчерпывающая аттака на подпись (R, S) для генерирования валидной лицензии кажется не такой уж и сложной. Давайте рассмотрим, как генерируется vlMac, подробнее. 1.3 Детально о Nyberg-Rueppel Из подписи (R, S), после ElbyCrypt.DecodePublic, Nyberg-Rueppel позволяет нам найти оригинальный мессадж. который был подписан. Вот, почему эта система подписи называется "с покрытием" ("with covering"). В CloneCD эта система использована на еллиптической кривой, с елементами поля GF(2^m). Схема Nyberg-Rueppel может быть использована на простых конечных группах, вида Z*_p. Если у вас сложности/непонимания с Z*_p - рекоммендую вам изучить самые основы Z*p_p (для примера - см. [3]). После изучения, все, связанное с еллиптическими кривыми, станет для вас более понятным. Если вас заинтересует использование криптографии еллиптических кривых - см. [4]. Процедура ElbyCrypt_DecodePublic - это cpVerify из Pegwit'а, но немного модифициованная. Вот эквивалент на Сях: void ElbyCrypt_DecodePublic(const vlPoint vlPublicKey, const vlPoint vlMac, cpPair * sig ) { ecPoint tl,t2; vlPoint t3,t4,p_order; vlCopy2( p_order, prime_order ); ecCopy( &tl, &curve_point ); ecMultiply( &tl, sig->s ); ecUnpack( &t2, vlPublicKey ); ecMultiplyC &t2, sig->r ); ecAdd( &tl, &t2 ); gfPack( tl.x, t4 ); vlRemainder( t4, p.order ); vlCopy( t3, sig->r ); if ( vlGreaterC t4, t3 ) ) vlAdd( t3, p_order ); vlSubtract( t3, t4 ); vlCopy( vlMac, t3 ); vlClear( p_order ); vlClearC t4 ); vlClearC t3 ); } В процедуру Pegwit'а - cpVerify, передается vlMac, и если возвращаемое значение 1 - если подписанный мессадж соответсвует vlMac, и 0 - если нет. Здесь, vlMac, инициализируется пустым. Процедура восстанавливает подписанный мессадж и копирует его в vlMac. Обе проверки используют CRC8, речь о котором шла выше, что также используется при проверке подписи. 1.3.1 Параметры Я думаю, что исходник без комментариев вам мало что скажет. Все вычисления понять легко, вот параметры: - кривая - эллиптическая кривая на GF(2^255). Эта группа представленая многочленами семнядцатой степени, чьи коеффициенты в GF(2^15). - точка - точка на кривой, назовем ее P. Ее порядок - n - простое число длинной 241 бит. - vlPublicKey - большое число, "сжатая" точка на кривой (данное большое число - абсцисса). Назовем эту точку Q. СТоит заметить, что порядок этой точки такой же как и у точки P. - sig - собссно подись, два больших числа (r, s), с помощью них также можно узнать сообщение, которое было подписано. - сообщение, чья подпись - (r, s), в виде большого числа. Назовем его h. 1.3.2 Проверка подписи Алгоритм проверки подписи не очень то и сложный: 1. Вычисляем X = s*P + r*Q 2. x = X.x (x - абсцисса точки X) 3. Вычисляем v = x mod n 4. Возвращаем сообщение, которое было подписано - h = r - v mod n 1.3.3 Подпись Алгоритм подписи не сложнее проверки =) 1. Выбираем случайное k в диапазоне [1, n-1] 2. Вычисляем k*P = (x, y) 3. r = x + h mod n 4. s = k - rd mod n 5. Подпись - (r, s) d - приватный ключ, такой что - Q = d*P и в диапазоне [1, n-1]. Чтобы подписать сообщение нам надо найти приватный ключ. Это проблема дискретного логарифма. Вычислить d "в лоб" на данный момент невозможно, учитывая что порядок P и Q - 241-битное число. Именно поэтому данная система защиты считалась неломаемой. Я покажу вам как можно вычислить приватный ключ. 1.4 Промежуточные выводы На данный момент мы знаем, что лицензия состоит из имени и NR подписи. Во-первых, есть 2 проверки сообщения - CRC первых 24 байт должна равняться дворду, который следует за этими байтами, и CRC имени должна равняться дворду, который начинается с 18 байта сообщения. Для того, чтобы сгенерировать лицензию, мы должны иметь все данные для подписи сообщения - тоесть приватный ключ. 2 Почему же эту защиту можно взломать Анализируя скарженные лицензии, мы находим серьезную ошибку в защите, что позволяет нам легко вычислить приватный ключ. 2.1 Анализ скарженных лицензий Мне удалось заполучить парочку лицензий для версии 2.8.4.1. Но в исследуемой версии эти лицензии были уже в блэклисте. В экспорте CCDDriver.dll есть функция CCDDriver_GetTable, которая возвращает массив 64-битных данных. Каждый член массива сравнивается с байтами сообщения (из лицензии) - от 15 до 22. Если совпадут - значит данная лицензия в блэклисте и не принимается. Вот два сериала (без имен, чтобы вы не могли использовать их в версии, где они еще не в блэклисте): 10542B69A0FDB37CBDD65A6D826C7763C0E1ED5F5EFF CB6DCCA9CF844F2284BEB10F68BF6FFC23E6E44A04CA 5F4B3CF57DCFB1DE01FFB4C0457E57BBD1001BBF 10F6101C94A78AF0B2AE709395408175C6983C7B51E3 185545282C92AA088FBEB10F3D9328ED23E7E44A04CA 5F4B9D067CC8AB69F2E2B4BFB6710252D1011BBF Раньше я упомянул, что из подписи мы можем узнать сообщение - h. Из этих двух сериалов - подписи (r1, s1) и (r2,s2) мы можем узнать: / r1 = 1BBFD10057BB457EB4C001FFB1DE7DCF3CF55F4B04CAE44A23E66FFC68BF | s1 = BEB12284844FA9CF6DCCFFCB5F5EE1ED63C06C776D82D65A7CBDFDB369A0542B \ h1 = 28012E44091E018B7132B3893A55BE7721656D206B637553330DAA9F / r2 = 1BBFD1010252B671B4BFF2E2AB697CC89D065F4B04CAE44A23E728ED3D93 | s2 = BEB1088F92AA282C5545E3187B51983C75C640819395AE70F0B2A78A1C94F610 \ h2 = 2801D8DB7A11018B6215AD14394F1E8821656D206B637553EBFE7F73 Дампы h1 и h2: 0E 00 9F AA 0D 33 53 75 63 6B 20 6D 65 21 77 BE ..??.3Suck me!w? 55 3A 89 B3 32 71 8B 01 1E 09 44 2E 01 28 U:%?2q<...D..( 0E 00 73 7F FE EB 53 75 63 6B 20 6D 65 21 88 1E ..s.??Suck me!?. 4F 39 14 AD 15 62 8B 01 11 7A DB D8 01 28 O9.-.b<..z??.( В глаза сразу же бросается строка "Suck me!". Она используется для расшифровки кода, который защищен ASProtect'ом. Я не буду подробно описывать эту часть защиты, но помните, что если там будет другая строка - части кода не будут расшифрованы и часть функций не будет работать. Без валидной лицензии было бы сложно найти такую строку =) Мы можем использовать одну из этих лицензий и обойти условный переход, чтобы программа считала, что лицензии нету в блэклисте. Далее мы можем отлаживать программу дальше, чтобы изучить код, который будет расшифрован. Может ли истечь срок использования сгенерированного ключа? Последняя проверка ключа происходит только если не закончился испытательный 21-дневный срок. loc_417CC0: ; CODE XREF: VerifyKey+12F xor ebx, ebx xor edi, edi mov eax, isNotExpired test eax, eax jz short loc_417D2E call dword_4C9890 ; ASProtect decryption jmp loc_417D28 ... Эта процедура может быть легко переписана на язык высшего уровня или просто рипнута. Из дворда, который начинается 15ым байтом сообщения, она вычисляет байт, который сравнивается с 6ым байтом. Для того чтобы уменьшить код кейген - я выбрал одно значение (0x87654321 -> 0x7A). Да - так создателю программы легко заблеклистить все сериалы из моего кейгена, но это ведь не проблема - мы можем каждый раз брать разный дворд и генерировать из него байт. 2.2 Вычисление приватного ключа Давайте вернемся к анализу NR. r = x + h mod p, где x - абсцисса k*P (P - точка, k - случайное число). Если разница между r1 и r2 очень мала, а размер h мал (по сравнению с r), то для вычисления r1 и r2 использовался один x. Тоесть k1*P = k2*P и, так как P - константа, k1=k2=k. Из процесса проверки следует: r1 - h1 = r2 - h2 Elby использует одно и то же k для подписи всех сообщений - это ОЧЕНЬ серьезная ошибка. Использование одного и того же k есть серьезной ошибкой практически для всех алгоритмов подписи, которые базируются на проблеме дискретного логарифма. Так как и d, и k играют важную роль при подписывании. Каждый раз надо использовать разное k, а также держать его в секрете. Но давайте вернемся к нашим уравнениям и подписям (r1, s1, h1) и (r2, s2, h2): / s1 = k1 - r1*d mod n \ s2 = k2 - r2*d mod n Если k1=k2: k2 = s1 + r1*d mod n Тогда второе уравнение можно переписать как: s2 = s1 + r1*d - r2*d mod n И мы можем найти d: d = (s2 - s1)*(r1 - r2)^(-1) mod n d = 5F4BA0012F2BCDA1CAC967DA0D004BF9F4AD25A28647433599FB23D991AC Все! Защита взломана и у нас есть возможность генерировать валидные лицензии. 3 Пишем кейген Я покажу вам мой алгоритм для генерации валидных лицензия на Сях. 3.1 Алгоритм генерации Вернемся к изучению h: 0E 00 73 7F FE EB 53 75 63 6B 20 6D 65 21 88 1E ..s.??Suck me!?. 4F 39 14 AD 15 62 8B 01 11 7A DB D8 01 28 O9.-.b?..z??.( Описание: - Первые два байта - размер h в вордах - всегда 0x000E - Байты с 3-ого по 5-ый - рандомные - 6-ой байт - 0x4B, вспомните про последнюю проверку ;) - Байты с 7-ого по 14-ый - строка для расшифровки кода ASProtect'ом - Байты с 15-ого по 18-ый - 0x87654321 - все та же последняя проверка - Байты с 19-ого по 22-ой - CRC имени - 23-ий и 24-ый байты - рандомные - Байты с 25-ого по 28-ой - CRC предидущих 24-х байт - 29-ый и 30-ый байты - должны равняться 0x01 и 0x28 - судя по лицензиям которые есть у меня =) Алгоритм генерации лицензии таков: - создаем большое число h, длинной 0x0E - копируем в него, начиная с 7-ого байта, строку для расшифровки кода - копируем в него, начиная с 29-ого байта, 0x0128 - вычисляем CRC имени - копируем в h, начиная с 19-ого байта, CRC имени - рандомно заполняем байты с 3-ого по 5-ый и с 23-ого по 24-ый =) - копируем в h нужный 6-ой байт и нужные байты с 15-ого по 18-ый - вычисляем CRC первых 24-х байт h - копируем в h, начиная с 25-ого байта, вычисленную CRC - вычисляем сигнатуру NR - (r, s) 3.2 Исходник В CloneCD использованая либа Pegwit, так что я тоже использовал ее. Стоит заметить, что если используется одно и то же k, то никаких вычислений на GF(2^m) производить не надо. Так как абсцисса k*P - константа. В моей реализации k - каждый раз разное, поэтому пришлось перекодить все. Вот код, который можно скомпилировать в Visual Studio 2005: #define _CRT_RAND_S #include #include #include "ec_crypt.h" const vlPoint private_key={15U, 0x91AC, 0x23D9, 0x99FB, 0x4335, 0x8647, 0x25A2, 0xF4AD, 0x4BF9, 0x0D00, 0x67DA, 0xCAC9, 0xCDA1, 0x2F2B, 0xA001, 0x5F4B }; const char asprKey[]="Suck me!"; void bigToAscii(char *str, vlPoint n) { int i; sprintf(str, "%02X", n[0]); for(i = 0; i < n[0]; i++) sprintf(str + 2 + 4 * i, "%04X", n[i + 1]); } void CookText(char *dest, char *src) { while(*src != 0) { if(*src != ' ') { if(*src >= 'A' && *src <= 'Z') *dest = *src - ('A' - 'a'); else *dest = *src; *dest++; } *src++; } *dest = 0; } void DoCRC8(char letter, unsigned int *crc) { int i; if(crc == NULL) return; for(i = 0; i < 8; i++) { char c = *crc; *crc >>= 1; if((c ^ letter) & 1 != 0) *crc ^= 0xC050A963; letter >>=1; } } void DoCRCString(char *str, unsigned int *crc) { *crc = ~0L; while(*str != 0) { DoCRC8(*str, crc); str++; } DoCRC8(0, crc); } unsigned int CalcUserCRC(char *name) { unsigned int crc; char *str=malloc(strlen(name)+1); CookText(str, name); DoCRCString(str, &crc); free(str); return crc; } void DoCRCData(int *crc, char *data, int len) { int i; *crc = ~0L; for(i = 0; i < len; i++) DoCRC8(*(data+i), crc); } void CreateLicense(char *name, char *serial) { vlPoint message={ 14U, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2801 }; vlPoint secret; cpPair signature; unsigned int crcUser, crcSerial, random_number; int i; char serialR[75], serialS[75]; for(i = 1; i < 24 / 2; i++) { rand_s(&random_number); message[i] = random_number; } /* захардкоженные зачнение для последней проверки */ message[2] = 0x7A00; message[7] = 0x4321; message[8] = 0x8765; /* выбираем случайное k*/ secret[0] = 15U; for(i = 1; i <= secret[0]; i++) { rand_s(&random_number); secret[i] = random_number; } /* вычислим CRC имени */ crcUser = CalcUserCRC(name); memcpy((char *)message + 18, &crcUser, sizeof(unsigned int)); /* вставим ключ для аспра */ memcpy((char *)message + 6, asprKey, strlen(asprKey)); /* вычисление CRC первых 24-х байт */ DoCRCData(&crcSerial, (char *)message, 24); memcpy((char *)message + 24, &crcSerial, sizeof(int)); /* подпись NR */ gfInit(); cpSign(private_key, secret, message, &signature); gfQuit(); /* сформируем сериал */ bigToAscii(serialS, signature.s); bigToAscii(serialR, signature.r); strcpy(serial, serialS); strcat(serial, serialR); } int main() { char serial[150]; char name[]="test"; srand(time(NULL)); CreateLicense(name, serial); printf("%s\n", serial); return 0; } 4 Выводы В этой статье я показал практическую атаку на систему, которая казалась неприступной. Все было сделано правильно - размер (241 бит) выбран верно, блэклист реализован (прим.пер - даже аспр заюзан =). Очень жаль, что всего лишь один параметр разрушил все! Сегодня многие программисты используют криптосистемы с открытыми ключами для защиты своих программ. Это правильный путь к неломаемой защите, но от ошибок никто не застрахован. Перед выпуском программы - автор должен оценить систеу защиты, выбранные параметры, время которое надо на взлом системы, если это возможно вообще. Но даже если все учтено, то ничто не мешает нам пропатчить - заменив параметры на свои - и закейгенить... Я хочу сказать спасибо SeVeN'у за то, что снабдил меня скарженными ключами, которые можно было найти в интернете. Без валидных лицензий я бы не написал всего этого. Также спасибо мемберам FRET, которые прочитали данную статью много раз перед выпуском ее в свет. 5 Литература [1] G. Barwood, Pegwit: www.george-barwood.pwp.blueyonder.co.uk/hp/v8/pegwit.htm [2] E. De Win, A. Bosselaers, S. Vandenberghe, P. De Gersem}, J. Vandewalle: A Fast Software Implementation for Arithmetic Operations in GF(2^n), Asiacrypt'96 [3] A. Menezes, P. van Oorschot, S. Vanstone: Handbook of Applied Cryptography, CRC Press, 1996 [4] D. Hankerson, A. Menezes, S. Vanstone: Guide to Elliptic Curve Cryptography, Springer-Verlag, 2004