Примеры реальных взломов: Intel C++ 7.0 Compiler — Архив WASM.RU
…компилятор Intel C++ 7.0 докачался глубокой ночью, часу где-то в пятом утра. Спать хотелось неимоверно, но и любопытство: была ли усилена защита или нет, тоже раздирало. Решив, что до тех пор пока не разберусь с защитой я все равно не усну, я, открыв новую консоль, и переустановив системные переменные TEMP и TMP на каталог C:\TEMP, наскоро набил неприлично длинное имя инсталлятора W_CC_P_7.0.073.exe в командной строке (необходимость в установке переменных TEMP и TMP объясняется тем, что в Windows 2000 они по умолчанию указывают на очень глубоко вложенный каталог, а инсталлятор Intel C++ - да и не только он - не поддерживает путей такого огромного размера).
Сразу же выяснилось, что политика защиты была кардинально пересмотрена и теперь наличие лицензии проверялось уже на стадии установки программы (в версии 5.x установка осуществлялось без проблем). ОК, даем команду dir и смотрим на содержимое того, с чем нам сейчас предстоит воевать:
Код (Text):
>dir Содержимое папки C:\TMP\IntelC++Compiler70 17.03.2003 05:10 <DIR> html 17.03.2003 05:11 <DIR> x86 17.03.2003 05:11 <DIR> Itanium 17.03.2003 05:11 <DIR> notes 05.06.2002 10:35 45 056 AutoRun.exe 10.07.2001 12:56 27 autorun.inf 29.10.2002 11:25 2 831 ccompindex.htm 24.10.2002 08:12 126 976 ChkLic.dll 18.10.2002 22:37 552 960 chklic.exe 17.10.2002 16:29 28 663 CLicense.rtf 17.10.2002 16:35 386 credist.txt 16.10.2002 17:02 34 136 Crelnotes.htm 19.03.2002 14:28 4 635 PLSuite.htm 21.02.2002 12:39 2 478 register.htm 02.10.2002 14:51 40 960 Setup.exe 02.10.2002 10:40 151 Setup.ini 10.07.2001 12:56 184 setup.mwg 19 файлов 2 519 238 байт 6 папок 886 571 008 байт свободноАга! Программа установки setup.exe занимает всего сорок с хвостиком килобайт. Очень хорошо! В такой объем серьезную защиту навряд ли спрячешь, а если даже так - этот крохотный файл ничего не стоит проанализировать целиком - до последнего байта дизассемблерного листинга. Впрочем, не факт, что защитный код расположен именно в setup.exe, он может находится и в другой месте, вот например… ChkLic.dll/ChkLic.exe, занимающими в совокупности немногим менее семисот килобайт. Постой, какой такой ChkLic? Это сокращение от Check License что ли?! Гм, у ребят из Intel очевидно серьезные проблемы с чувством юмора. Уж лучше бы они назвали этот файл "Hack Me" честное слово! Ладно, судя по объему, ChkLic это тот самый FLEX lm и есть, а с ним мы уже сталкивались (см. "Intel C++ 5.0 Compiler") и приблизительно представляем как его ломать.
Даем команду "dumpbin /EXPORTS ChkLic.dll" для исследования экспортируемых функций и… крепко держимся за Клаву, чтобы не упасть со стула:
Код (Text):
Dump of file ChkLic.dll File Type: DLL Section contains the following exports for ChkLic.dll 0 characteristics 3DB438B4 time date stamp Mon Oct 21 21:26:12 2002 0.00 version 1 ordinal base 1 number of functions 1 number of names ordinal hint RVA name 1 0 000010A0 _CheckValidLicenseЧерт побери! Защита экспортирует всего одну-единственную функцию с замечательным именем CheckValidLicense. "Замечательным" - потому, что назначение функции становится понятным ее названия и появляется возможность избежать кропотливого анализа дизассемблерного кода. Ну вот, отбили весь интерес… уж лучше бы они ее по ординалу экспортировали что ли, или, по крайней мере, окрестили ее каким ни будь отпугивающим именем типа DES Decrypt.
…размечтались! Ладно, вернемся к нашим баранам. Давайте рассуждать логически: если весь защитный код сосредоточен непосредственно в ChkLic.dll (а, судя по "навесному" характеру защиты, это действительно так), то вся "защита" сводится к вызову CheckValidLicense из Setup.exe и проверке возращенного ею результата. Поэтому для "взлома" достаточно лишь пропадчить ChkLic.dll, заставляя функцию ChekValidLicense всегда возвращать… да, кстати, что она должна возвращать? Точнее: какое именно возвращаемое значение соответствует успешной проверки лицензии? Нет, не торопитесь дизассемблировать setup.exe для определения, ведь возможных вариантов не так уже и много: либо FALSE, либо TRUE. Вы делаете ставку на TRUE? Что ж, в каком-то смысле это логично, но с другой стороны: а почему мы, собственно, решили, что функция CheckValidLicense возвращает именно флаг успешности операции, а не код ошибки? Ведь должна же она как-то мотивировать причины отказа устанавливать компилятор: файл с лицензией не найден, файл поврежден, лицензия просрочена и так далее? Хорошо, попробуем возвратить ноль, а если это не прокатит, возвратим единицу.
ОК, пристегивайтесь, поехали! Запускаем HIEW, открываем файл ChkLic.dll (если же он не открывается - трижды помянув сусликов, временно скопируем его в корневую или любую другую директорию, не содержащую в своем имени спецсимволов, которые так не нравятся hiew'у). Затем, обратившись еще раз к таблице экспорта, полученной с помощью dumpbin, определяем адрес функции CheckValidLicense (в данном случае 010A0h) и через
, "10A0" переходим в ее начало. Теперь, - режем по "живому", перезаписывая поверх старого кода "XOR EAX, EAX/RETN 4". Почему именно "REN 4", а не просто "RET"? Да потому, что функция поддерживает соглашение stdcall, о чем можно узнать взглянув в HIEW'e на ее эпилог (просто пролистывайте экран дизассемблера вниз до тех пор, пока не встретите RET). Проверяем… Это работает!!! Несмотря на отсутствие лицензии, инсталлятор, не задавая лишних вопросов, начинает установку! Стало быть, защита пала. Ой, не верится нам, что все так просто и, чтобы не сидеть, тупо уставившись в монитор в ожидании завершения процесса инсталляции программы, мы натравливаем на setup.exe свой любимый дизассемблер IDA. Первое, что бросается в глаза, отсутствие CheckValidLicense в списке импортируемых функций. Может быть, она файл ChkLic.exe как-то запускает? Пробуем найти соответствующую ссылку среди автоматически распознанных строк: "~View аNames", "ChkLic"… ага, строки "Chklic.exe" здесь вообще нет, но зато обнаруживается "Chklic.dll". Ага, понятно, значит, библиотека ChkLic загружается явной компоновкой через LoadLibrary. И переход по перекрестной ссылке подтверждает это:
.text:0040178 ; если функция возвратила не ноль, то выходим из программы установкиКод (Text):
.text:0040175D push offset aChklic_dll ; lpLibFileName .text:00401762 call ds:LoadLibraryA .text:00401762 ; загружаем ChkLic.dll ^^^^^^^^^^^^^^^^^ .text:00401762 ; .text:00401768 mov esi, eax .text:0040176A push offset a_checkvalidlic ; lpProcName .text:0040176F push esi ; hModule .text:00401770 call ds:GetProcAddress .text:00401770 ; получаем адрес функции CheckValidLicense .text:00401770 ; .text:00401776 cmp esi, ebx .text:00401778 jz loc_40192E .text:00401778 ; если такой библиотеки нет, то выходим из программы установки .text:00401778 ; .text:0040177E cmp eax, ebx .text:00401780 jz loc_40192E .text:00401780 ; если такой функции в библиотеке нет, то выходим из установки .text:00401780 ; .text:00401786 push ebx .text:00401787 call eax .text:00401787 ; вызываем функцию ChekValidLicense .text:00401787 ; .text:00401789 test eax, eax .text:0040178B jnz loc_4019A3Невероятно, но эта до ужаса примитивная защита построена именно так! Причем, полуметровый файл ChkLic.exe вообще не нужен! И чего ради стоило тащить его из Интернета? Кстати, если вы надумаете сохранять дистрибьютив компилятора (внимание: я не говорил "распространять"!), то для экономии дискового места ChkLic.* можно стереть: либо пропадчив setup.exe, навсегда отучив его к ним обращаться, либо же просто создав свою собственную ChkLic.dll, экспортирующую stdcall функцию CheckValidLicence вида: int CheckValidLicence(int some_flag) { return 0;}
Так-с, пока мы все это обсуждали, инсталлятор закончил установку компилятора и благополучно завершил свою работу. Интересно ли запустится ли компилятор или все самое интересное только начинается? Лихорадочно спускаемся вниз по разветвленной иерархии вложенных папок, находим icl.exe, который как и следовало ожидать, находится в каталоге bin, нажимаем
и… Компилятор естественно не запускается, ссылаясь на то, что "icl: error: could not checkout FLEX lm license", без которой он не может продолжить свою работу. Выходит, что Intel применила многоуровневую защиту и первый уровень оказался грубой защитой от дураков. Что ж! Мы принимаем этот вызов и, опираясь на свой предыдущий опыт, машинально ищем файл LMGR*.DLL в каталоге компилятора. Бесполезно! На этот раз такого файла здесь не оказывается, зато выясняется, что icl.exe сильно прибавил в весе, перевалив за отметку шестиста килобайт… Стоп! А не прилинковали ли разработчики компилятора этот самый FLEX lm статической компоновкой? Смотрим: в Intel C++ 5.0 сумма размеров lmgr327.dll и icl.exe составляла 598 Кб, а сейчас одни лишь icl.exe занимает 684 Кб. С учетом поправки на естественное старческое "ожирение", цифры очень хорошо сходятся. Значит, все-таки FLEX lm! Ой-ой! А ведь теперь, - без символических имен функций, ломать защиту будет намного труднее… Впрочем, не будем раньше времени паниковать! Давайте думать, только спокойно! Навряд ли команда разработчиков полностью переписала весь код, взаимодействующей с этой "конвертной" защитой. Скорее всего, ее "усовершенствование" одной лишь сменой типа компоновки и закончилось. А раз так, то шансы взломать программу по прежнему велики!
Памятуя о том, что в прошлый раз защитный код находится в функции main, мы, определив ее адрес, просто устанавливаем точку останова и, дождавшись всплытия отладчика, тупо трассируем код, попеременно поглядывая то на отладчик, то на окно вывода программы: не появилась ли там ругательное сообщение? При этом, все встретившиеся нам условные переходы, мы отмечаем на отдельном листке бумаги (или откладываем в своей собственной памяти, если вы так хотите), не забыв указать выполнялся ли каждый условный переход или нет… Стоп! Что-то заболтались мы с вами, а ведь ругательное сообщение уже выскочило! ОК, хорошо! Посмотрим, какой условный переход ему соответствовал. Наши записи показывают, что последним встретившимся переходом, был условный переход JNZ, расположенный по адресу 0401075h и "реагирующий" на результат, возращенной процедурой sub_404C0E:
Код (Text):
.text:0040106E call sub_404C0E .text:00401073 test eax, eax .text:00401075 jnz short loc_40107F .text:00401077 mov al, 1 .text:00401079 mov byte ptr [esp+40h+var_18], al .text:0040107D jmp short loc_4010BA .text:0040107F ; --------------------------------------------------------------------- .text:0040107F .text:0040107F loc_40107F: ; CODE XREF: _main+75^j .text:0040107F mov eax, offset aFfrps ; "FFrps" .text:00401084 mov edx, 21h .text:00401089 call sub_404C0E .text:0040108E test eax, eax .text:00401090 jnz short loc_40109AОчевидно, что sub_404C0E и есть та самая защитная процедура, которая осуществляет проверку лицензии на ее наличие. Как ее обхитрить? Ну, тут много вариантов… Во-первых, можно, вдумчиво и скрупулезно проанализировать содержимое sub_404C0E на предмет выяснения: что именно и как именно она проверяет. Во-вторых, можно просто заменить JNZ short loc_40107F на JZ short loc_40107F или даже NOP, NOP. В-третьих, команду проверки результата возврата TEST EAX, EAX можно превратить в команду установки нуля: XOR EAX, EAX. В-четвертых, можно пропадчить саму sub_404C0E, чтобы она всегда возвращала ноль. Не знаю, как вы, но мне больше всех приглянулся способ номер три. Меняем два байта и запускаем компилятор. Если никаких других проверок его "лицензионности" в защите нет, то программа заработает и, соответственно, наоборот. (Как мы помним, в пятой версии таких проверок было две). Поразительно, но компилятор больше не ругается и работает!!! Действительно, как и следовало ожидать, его разработчики ничуть не усилили защиту, а, напротив, даже ослабили ее! © Крис Касперски
Примеры реальных взломов: Intel C++ 7.0 Compiler
Дата публикации 4 апр 2003