Примеры реальных взломов: компилятор Intel С++ 5.0.1 — Архив WASM.RU

Все статьи

Примеры реальных взломов: компилятор Intel С++ 5.0.1 — Архив WASM.RU

Прежде, чем приступать к обсуждению аспектов стойкости защиты компилятора Intel C++ 5.0.1, считаю своим долгом заявить, что я глубоко восхищен этим великолепным программным продуктом и ломать его на мой взгляд по меньшей мере кощунственно. Впрочем, сегодня только ленивый не найдет в Сети кряк (один только Google по запросу "Intel C++ crack" выдает свыше 12 тысячи ссылок!), так что никакого вреда от данной публикации не будет.

Немного грустных новостей для начала. Приобрести легальную версию данного компилятора для жителей России оказывается чрезвычайно затруднительно. И вопрос упирается даже не в то "сколько он стоит" (а стоит он, если мне не изменяет память что-то в районе тысячи долларов), - компания Intel просто игнорирует данный сегмент рынка. Обращения в российское представительство компании с просьбой предоставить (за деньги!) данный компилятор для его же описания (читай - рекламы и продвижения) в книге "Техника оптимизации программ" положительных результатов не дали, - даже после того, как к этому вопросу подключились прямо-таки скажем не мелкие отечественные издательства BHV и Солон - Р. Ладно, не хотят продавать - ну и не надо! Благо хоть с сервера компании можно свободно утянуть 30-дневный триал. Негусто конечно, но для сравнительного тестирования - вполне достаточно (а для других целей мне этот компилятор и не нужен!).

Впрочем, все оказалось не так просто! С web-сервера компилятор за просто так не отдался, - после заполнения регистрационной формы меня вежливо поблагодарили и сообщили, что сейчас ко мне на мыло упадет письмо с триальной лицензией и инструкцией по ее установке. Это "сейчас" заняло у севера аж несколько дней (такое впечатление, что анкеты просматриваются вручную). ОК! Лицензия получена! Начинаем скачивать файл…. Как это так докачка не поддерживается?! А вот не поддерживается и все! Учитывая, что у меня лишь хлипкий Dial-Up по каналу в 19.200 (да и тот по межгороду) скачать полста мегабайт без единого разрыва просто нереально. К тому же, работа над книгой уже близиться к завершению и вносить в нее еще один компилятор (а, значит, - переписывать кучу текста заново) мне становится просто в лом. Да и Intel C++ это далеко не самый популярный в кругах российских программистов компилятор и книга без него как ни будь уж переживет (хотя, посмотреть как Intel оптимизирует код под свои процессоры очень хотелось, да и документация по компилятору вдохновляла) .

Разозлившись на весь свет (и на парней из Intel в частности) я отправился на ftp-сервер компании, откуда наскоро, всего за каких-то три дня, слил полнофункциональную (хотя и шибко несвежую) версию компилятора, находящуюся по следующему адресу: ftp://download.intel.com/software/products/downloads/C5.0.1-15.exe. (приятно, что ftp докачку исправно поддерживал и многократные разрывы никаких проблем не вызывали). Польстившись на размер, я скачал именно пятую версию компилятора, которая была в полтора раза легче шестой (под которую у меня имелась неиспользованная триальная лицензия) и аж в два раза компактнее седьмой - новейшей на момент написания этих строк - версии, ломать которую из "политических" соображений я все равно бы не рискнул, так зачем же ее зря качать?

Теперь, собственно, мы и подходим к известному философскому вопросу: этично ли ломать программный продукт уважаемой тобой компании или без этого можно обойтись? Да если бы без этого было возможно обойтись, я бы - честное слово - без тени сожаления выложил за этот замечательный продукт пачку вечнозеленых, но, увы… компания не проявляет ко мне как покупателю никакого интереса и, кроме как ломать, ничего другого просто не остается!

Итак, инсталлируем Intel C++ и, предварительно скопировав в просроченную лицензию от шестой версии в папку \Intel\Licenses, запускаем головной файл программы:

...\Program Files\Intel\C501\Compiler50\ia32\bin>icl.exe
Intel(R) C++ Compiler for 32-bit applications, Version 5.0.1   Build 010525Z
Copyright (C) 1985-2001 Intel Corporation.  All rights reserved.

icl: error: could not checkout FLEXlm license
checkout failed: No such feature exists (-5,357)

Как и следовало ожидать "could not checkout FLEX lm license" ("не могу проверить FLEX lm лицензию") - компилятор ругается и прекращает свою работу. Ага, стало быть, программа защищена FLEX'ом, - достаточно известным в хакерских кругах менеджером лицензий от компании Globetrotter Inc, представляющим собой достаточно продвинутую защиту интегрированного типа. Разработчик защищаемого приложения получает в свое распоряжение SDK, содержащее как тривиальные функции проверки валидности ключевого файла (лицензии), так и развитые средства динамической шифровки файла. При грамотном подходе к защите запустить защищенную программу без наличия соответствующей ей лицензии доподлинно невозможно. Если часть программы зашифрована, - пытаться расшифровать ее без ключа - дохлое дело. Правда не факт, что парни из Intel действительно использовали шифрование, к тому же, зашифрованные фрагменты иногда удается восстановить по косвенным данным. Это смотря, что еще зашифровано!

Разумеется, при наличии триальной лицензии шифровка снимается без труда, но в том-то все и дело, что триальной лицензии у меня не было! Тем не менее надежда меня не покидала и, перекусив для смелости батоном докторской колбасы, сдобренной значительным количеством кетчупа, я запустил свой любимый дизассемблер IDA, и… не знаю у кого как, а у меня вид консольной IDA, распахнутой на весь экран, всегда вызывает чувство благоговения. ОК, ну-ка посмотрим где скрываются те текстовые строки, которые выводятся при отсутствии лицензии на экран. Результат: ни "No such feature exists", ни "could not checkout" в ASCII-строках (т. е. тех строках, что сумел распознать автоматический анализатор IDA) не найдено. Хорошо, зайдем с другого конца: нажимам для переключения в hex-режим и давим для поиска текстовых строк в "сыром" виде. Что ж, на этот раз поиск "could not checkout" увенчался успехом!

.data1:0042D9C0  63 6F 75 6C 64 20 6E 6F-74 20 63 68 65 63 6B 6F "could not checko"
.data1:0042D9D0  75 74 20 46 4C 45 58 6C-6D 20 6C 69 63 65 6E 73 "ut FLEXlm licens"
.data1:0042D9E0  65 00 00 00 63 6F 75 6C-64 20 6E 6F 74 20 6C 6F "e...could not lo"
.data1:0042D9F0  63 61 74 65 20 46 4C 45-58 6C 6D 20 72 65 67 69 "cate FLEXlm regi"
.data1:0042DA00  73 74 72 79 20 6B 65 79-00 00 00 00 63 6F 75 6C "stry key....coul"

Нажимаем еще один раз для возврата в режим дизассемблера, подводим курсор к адресу 42D9C0h и нажимаем для преобразования цепочки байт в ASCII-строку. В результате мы получаем:

.data1:0042D9C0 aCouldNotChecko db 'could not checkout FLEXlm license',0

А как узнать: кто же выводит строку-ругательство на экран? Нет ничего проще! Вновь переключившись в режим дизассемблера по , давим для поиска последовательности "C0 D9 40 00" - адрес строки, представленный в обратном (с учетом порядка следования старших байтов) виде. Опа! Мы видим код наподобие следующего:

.data:00420CE8                 db 50h
.data:00420CE9                 db 0DEh ; ¦
.data:00420CEA                 db  42h ; B
.data:00420CEB                 db    0 ;
.data:00420CEC                 db    1 ;
.data:00420CED                 db    0 ;
.data:00420CEE                 db    0 ;
.data:00420CEF                 db    0 ;
.data:00420CF0                 db 2Ch
.data:00420CF1                 db 0DEh ; ¦
.data:00420CF2                 db  42h ; B
.data:00420CF3                 db    0 ;
.data:00420CF4                 db    2 ;
.data:00420CF5                 db    0 ;
.data:00420CF6                 db    0 ;
.data:00420CF7                 db    0 ;

Косвенный вызов строки! Ну, собственного, этого и следовало ожидать (иначе с чего бы это автоматических анализатор IDA их не распознал?). Хорошо, преобразуем двойные слова в смещения, руководствуясь при этом тем, что число "42h" должно выпадать на младший байт старшего слова (иначе, адрес ссылки уйдет за диапазон предельно допустимых значений) и получаем:

.data:00420DE8 dd offset aCouldNotLoca_0 ; "could not locate FLEXlm registry direct"
.data:00420DEC dd 21h
.data:00420DF0 dd offset aCouldNotLocate ; "could not locate FLEXlm registry key"
.data:00420DF4 dd 22h
.data:00420DF8 dd offset aCouldNotChecko ; "could not checkout FLEXlm license"
.data:00420DFC dd 23h

Попробуем теперь найти ту су…, в общем тот код, что обращается к указателю на ругательную строку, расположенному по адресу 420CE8h? Не надо спешить! По виду полученной таблицы смещений можно с уверенностью заключить, что прямого обращения к ее элементам не будет. Можно предположить, что числа, стоящие возле ссылок на строки - это коды ошибок, а сами строки - соответствующие тексты сообщений. Если так, то с вероятностью близкой к единице, разработчиками программы использовалась относительная адресация, т. е. для вычисления эффективного адреса элемента ее смещение в таблицы суммируются с базовым адресом таблицы, - единственным адресом, который загружается явно.

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

.data:00420CDE              db    0 ;
.data:00420CDF              db    0 ;
.data:00420CE0 off_420CE0   dd offset unk_42DE80    ; DATA XREF: sub_403370+5E^r
.data:00420CE4 dword_420CE4 dd 0                    ; DATA XREF: sub_403370+19^r
.data:00420CE4                                      ; sub_403370+39^r
.data:00420CE8              dd offset aCouldNotFindDi ; "could not find directory"
.data:00420CEC              dd 1

Ага! Есть две перекрестных ссылки! Это хорошо! Теперь поднимемся по ним вверх, - прямиком к вызывающему их коду? Можно, конечно, поступить и так, но есть и более универсальное решение: запустив Soft-Ice, мы устанавливаем точку останова на чтение ячейки 420DE8h (если вы еще не забыли - это адрес элемента таблицы, ссылающийся на искомую ругательную строку). Теперь - кто бы к ней не обращался, Soft-Ice обязательно всплывет, и ведь действительно он всплывает! Пару раз отдав команду "P RET", поднимающую нас из дебрей глубоко вложенных процедур поближе к свету. Наконец, мы взбирается на вершину стека и очередной "P RET" приводит к завершению программы. ОК, повторяем все заново, делая на этот раз на один "P RET" меньше. Записываем любой из близлежащих адресов (пусть это будет для определенности адрес 4031C4h) и натравливаем на него IDA.

.text:004031C4      call    lc_checkout
.text:004031C9      test    eax, eax
.text:004031CB      jz      short loc_403215
.text:004031CD      cmp     eax, 0FFFFFFF6h
.text:004031D0      jz      loc_41B000
.text:004031D6      cmp     eax, 0FFFFFFB7h
.text:004031D9      jz      loc_41B01A
.text:004031DF
.text:004031DF loc_4031DF:                             ; CODE XREF: .text1:0041B015vj
.text:004031DF                                         ; .text1:0041B026vj
.text:004031DF      mov     [esp+240h+var_240], 23h
.text:004031E6      call    sub_405B00
.text:004031EB      mov     eax, dword_424C9C
.text:004031F0      mov   [esp+240h+var_240], eax
.text:004031F3      mov [esp+240h+var_23C], offset aCheckoutFailed ; "checkout failed"
.text:004031FB      call  lc_perror
.text:00403200      mov   eax, dword_424C9C
.text:00403205      mov   [esp+240h+var_240], eax
.text:00403208      call  lc_get_errno
.text:0040320D      mov   [esp+240h+var_240], eax
.text:00403210      call  sub_405BA0
.text:00403215
.text:00403215 loc_403215:                             ; CODE XREF: sub_403000+1CB^j
.text:00403215      mov   eax, dword_424C9C
.text:0040321A      mov   edx, dword_421E3C
.text:00403220      mov   [esp+240h+var_240], eax
.text:00403223      mov   [esp+240h+var_23C], edx
.text:00403227      call  lc_auth_data
.text:0040322C      mov   edx, eax
.text:0040322E      mov   eax, dword_424C9C
.text:00403233      call  sub_40A6F8
.text:00403238

Вот это да! - восклицаем мы, пришибленно уставившись на экран. Многое мы ожидали от IDA, но вот чтобы она так запросто представила символьные имена защитных функций, все говорящие за себя: lc_chekout, lc_perror, lc_auth_data… Черт, возьми, как? Вдохновленные смутной надеждой мы неуверенно подгоняем курсор к lc_chekout и нажимаем на .

.idata:0041D12C ; Imports from LMGR327A.dll
.idata:0041D12C ;
.idata:0041D12C extrn __imp_lc_init:dword ; DATA XREF: lc_init^r
.idata:0041D130 extrn __imp_lc_expire_days:dword
.idata:0041D130   					; DATA XREF: lc_expire_days^r
.idata:0041D134 extrn __imp_lc_free_job:dword ; DATA XREF: lc_free_job^r
.idata:0041D138 extrn __imp_lc_checkin:dword ; DATA XREF: lc_checkin^r
.idata:0041D13C extrn __imp_lc_auth_data:dword ; DATA XREF: lc_auth_data^r
.idata:0041D140 extrn __imp_lc_get_errno:dword ; DATA XREF: lc_get_errno^r
.idata:0041D144 extrn __imp_lc_perror:dword ; DATA XREF: lc_perror^r
.idata:0041D148 extrn __imp_lc_checkout:dword ; DATA XREF: lc_checkout^r
.idata:0041D14C extrn __imp_lc_set_attr:dword ; DATA XREF: lc_set_attr^r
.idata:0041D150

Святой Кондратий! И это они еще называют защитой?! Все защитные функции вынесены в отдельную динамическую библиотеку (наверное, чтобы взломщику разбираться было легче?) - LMGR327A.DLL, в названии которой угадывается "Library ManaGeR", причем, это штатные функции FLEX lm, описание которых можно найти в его же SDK (хоть SDK на FLEX ln с компилятором и не поставляется, найти его в Сети - плевое дело).

Отыскав в текущем каталоге этот самый LMGR327A.DLL, мы открываем его HIEW'ов на предмет полного переписывания функции lc_checkout. Ну, насчет "переписывания" автор, ясное дело, загнул. Всего-то и требуется, - заставить lc_checkout всегда возвращать нуль, для чего первые две команды ее тела должны выглядеть приблизительно так: "XOR EAX, EAX / RETN". Записываемся и с дрожью в сердце, запускам icl.exe на выполнение. Критическая ошибка приложения? А чего мы хотели?! Ведь теперь функция lc_auth_data получает неверные данные и гробит все к черту. Впрочем, не будет спешить. Беглое исследование процедуры sub_40A6F8 как будто не выявляет никаких следов шифрования и поэтому ее можно смело удалить, не забыв тоже самое "на всякий пожарный" случай проделать и с lc_auth_data (самое простое - впихнуть в ее начало RETN). Сохраняемся, запускам icl.exe и… компилятор работает! Все! Больше тут нечего ломать!

Самое забавное, что размер защитного механизма (413 Кб) в два с половиной раза превышает размер защищенной с его помощью программы (176 Кб)! Как говорится - no comment.

2002-2013 (c) wasm.ru