allasm.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 и смотрим на содержимое того, с чем нам сейчас предстоит воевать:

>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" для исследования экспортируемых функций и… крепко держимся за Клаву, чтобы не упасть со стула:

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: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
.text:0040178 ; если функция возвратила не ноль, то выходим из программы установки

Невероятно, но эта до ужаса примитивная защита построена именно так! Причем, полуметровый файл 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: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, чтобы она всегда возвращала ноль. Не знаю, как вы, но мне больше всех приглянулся способ номер три. Меняем два байта и запускаем компилятор. Если никаких других проверок его "лицензионности" в защите нет, то программа заработает и, соответственно, наоборот. (Как мы помним, в пятой версии таких проверок было две). Поразительно, но компилятор больше не ругается и работает!!! Действительно, как и следовало ожидать, его разработчики ничуть не усилили защиту, а, напротив, даже ослабили ее!

  [C] Крис Касперски