SentinelLM!WlscGen_CRK_2 — Архив WASM.RU
Дорогие коллеги, предлагаю вашему вниманию заключительную статью из цикла о SentinelLM. Вы уже знаете, что от полного счастья нас отделяет одна единственная функция SLM API. Итак, пришло время нанести смертельный удар по SLM SDK, т.е. пропатчить функцию RNBOsproQuery.
Я наивно полагаю, что вы уже изучили "SentinelSuperPro 6.0 Developer's Guide" и прекрасно представляете себе работу этой функции, но "повторение -- мать учения" (© русский народ). RNBOsproQuery уподобается чёрному ящику, в который поступают произвольные данные и выходят данные не менее произвольные. Количество байт во входном массиве равно количеству байт в выходном. Последние 4 байта выходного массива дублируются в 32-битной ячейке памяти.
RNBOsproQuery(packet, address, *queryData, *response, *response32, length)
address -- это номер ячейки донгла, с которой начинается алгоритм; queryData -- входной массив; response -- выходной массив; response32 -- последние 4 байта выходных данных, представленные в виде 32-битной переменной; length -- длинна (в байтах) как входного, так и выходного массивов.
Чтоб окончательно вас запутать, далее представлен "графический эквивалент" описания RNBOsproQuery:
А что всё-таки там за алгоритм кроется в недрах донгла? Каким образом выходные данные связаны со входными? Кто застрелил Дж. Ф. Кеннеди? Что было вначале: яйцо или курица? Надеюсь, вы теперь понимаете почему хак RNBOsproQuery порой представляет собой изрядную сложность даже для опытного реверсера.
CyberHeg в своей статье предлагает очень интересный, но довольно сложный, подход к решению этой проблемы. Правда, в случае с WlscGen.exe обращения к RNBOsproQuery осуществляются в стиле
Код (Text):
if(!isQueryOK()) goto fig_vam;т.е. пропатчить этот код не просто, а очень просто! Причём RNBOsproQuery вызывается всего лишь 4 раза и обработка результата почти одинакова во всех четырёх случаях. Итак, грузим WlscGen.exe в IDA, применяем соответствующие сигнатуры и задаём поиск вызовов RNBOsproQuery. Вот вам и первый результат:
Код (Text):
00423611 loc_423611: 00423611 xor ebx, ebx 00423613 push ebx 00423614 call _time 00423619 add esp, 4 0042361C push eax 0042361D call _srand 00423622 add esp, 4 00423625 call _rand <span class=y>; Генерируем случайное число (X)</span> 0042362A cdq 0042362B mov ecx, 0Ah 00423630 idiv ecx <span class=y>; dx = X%10 (остаток от деления)</span> 00423632 movzx eax, dx 00423635 imul eax, 9 00423638 mov [var_A], dx <span class=y>; Сохраняем случайное число</span> . . . 00423659 lea ecx, [var_18] 0042365C lea eax, [var_94] 00423662 lea edx, [var_D0] 00423668 push 4 <span class=y>; length</span> 0042366A push ecx <span class=y>; *response32</span> 0042366B push eax <span class=y>; *response</span> 0042366C lea ecx, [var_4D4] 00423672 push edx <span class=y>; *queryData</span> 00423673 push 0Ch <span class=y>; address</span> 00423675 push ecx <span class=y>; packet</span> 00423676 call <strong>_RNBOsproQuery@24</strong> 0042367B lea ecx, [var_58] 0042367E lea edx, [var_94] 00423684 push ecx 00423685 push 4 00423687 push edx ; Конвертируем response32 в строку 00423688 call sub_42394F 0042368D add esp, 0Ch 00423690 lea ecx, [var_58] ; Извлекаем наше случайное число 00423693 movzx eax, [var_A] 00423697 imul eax, 9 0042369A push 4 0042369C add eax, OFFSET a3a70b51a 004236A1 push eax 004236A2 push ecx 004236A3 call <strong>_strncmp</strong> <span class=y>; Оно или не оно?</span> 004236A8 add esp, 0Ch 004236AB mov edi, eax 004236AD test edi, edi 004236AF jz short loc_4236C4 <span class=y>; Оно!!!</span>Что делает этот код? Ладно, объясняю:
- генерируем случайное число стандартным сишным методом;
- колдуем над этим числом и переводим его в массив;
- подсовываем этот массив в RNBOsproQuery для обработки алгоритмом 0Ch (адрес начальной ячейки алгоритма);
- результат обработки конвертируем в ASCII-строку;
- используем случайное число из пункта 1 для адресации фиксированного в экзешнике массива со строковыми константами;
- сравниваем обе строки;
- если строки совпадают, продолжаем (последний переход JZ).
Тот фиксированный массив в программе является своеобразным рангом алгоритма 0Ch, т.е. он хранит все возможные результаты данного алгоритма. Я рекомендую вам пройтись по данному участку кода под отладчиком и убедиться в тривиальности всей проблемы. Что нам мешает исправить тот последний переход? Ну, вообще говоря, правильнее будет поксорить регистр EAX на месте вызова _strncmp, как будто строки равны. Заодно можно убрать вызов RNBOsproQuery чтоб лишний раз не беспокоить драйвер. Тривиальный патч:
Код (Text):
00423668 push 4 0042366A push ecx 0042366B push eax 0042366C lea ecx, [var_4D4] 00423672 push edx 00423673 push 0Ch 00423675 push ecx 00423676 <span class=b>83C418</span> add esp, 18h <span class=y>; Восстанавливаем стек</span> 00423679 <span class=b>90</span> nop <span class=y>; вместо вызова RNBOsproQuery</span> 00423679 <span class=b>90</span> nop 0042367B lea ecx, [var_58] 0042367E lea edx, [var_94] 00423684 push ecx 00423685 push 4 00423687 push edx 00423688 call sub_42394F 0042368D add esp, 0Ch 00423690 lea ecx, [var_58] 00423693 movzx eax, [var_A] 00423697 imul eax, 9 0042369A push 4 0042369C add eax, OFFSET a3a70b51a 004236A1 push eax 004236A2 push ecx 004236A3 <span class=b>33С0</span> xor eax, eax <span class=y>; обнуляем EAX</span> 004236A5 <span class=b>90</span> nop <span class=y>; вместо вызова strncmp</span> 004236A6 <span class=b>90</span> nop 004236A7 <span class=b>90</span> nop 004236A8 add esp, 0Ch 004236AB mov edi, eax 004236AD test edi, edi 004236AF jz short loc_4236C4Остальные 3 вызова RNBOsproQuery фиксятся аналогичным образом. Справедливости ради стоит заметить что подобных глюков (иначе и не назовёшь явное сравнение через _strncmp) вы больше не увидите. RNBOsproQuery кроет в себе громадный потенциал, который по достоинству ценят лишь немногие пользователи SLM, хакеры и мы с вами. Чего только нельзя сделать используя ячейки с алгоритмами, содержание которых недоступно реверсеру?! Даже идея со случайным числом, хеш-алгоритмами и массивом строк можно было бы назвать удачной, если б не то [нецензурный эпитет] сравнение сразу после RNBOsproQuery.
Ну вот и всё: WlscGen.exe к употреблению готов! Попробуйте создать пробную лицензию (задайте параметры по вашему усмотрению) и убедитесь в работоспособности генератора. Получилось? Тогда достаньте ваш любимый патчегенератор (Patch Creation Wizard, например) и сделайте универсальный патч для WlscGen.exe (не фиксить же его вручную при каждой смене VId?!). Не распространяйте ваш патч потом в сети, ладно?
Давайте подытожим наши знания. Для этого рассмотрим два типичных случая использования SLM.
1. SentinelSuperPro API
Пример: WlscGen.exe.
Документация: "SentinelSuperPro 6.0 Developer's Guide".
Приложение использует функции работы с донглом: RNBOsproRead, RNBOsproWrite, RNBOsproQuery и т.д. Задача исследователя заключается в обнаружении и последующем обезвреживании данных функций, как мы и поступили с WlscGen. Данный случай можно назвать низкоуровневой защитой (приложение явно опирается на архитектуру донгла).2. SLM Client API и иже с ним
Пример: мой таргет и все-все-все.
Документация: "SentinelLM Programmer's Reference Manual", "SentinelLM Developer's Guide".
Приложение использует высокоуровневые функции, вроде LSRequest. Всю грязную работу (чтение лицензионного файла, общение с донглом и т.д.) выполняет API. Обратите внимание на то, что API может и не обращаться к донглу если лицензионный файл того не требует. Типичный подход к решению проблемы требует получения VId жертвы и некоторой информации о самой лицензии (тип лицензии, версия лицензируемой фичи, наименование фичи и т.д.) Полученная информация передаётся в WlscGen и создаётся новая лицензия (без триального срока... без донгла... Стоп! А откуда взять эту дополнительную информацию о самой лицензии? В самом простом случае её можно выудить из оригинального лицензионного файла. Имя по умолчанию для лиц. файлов -- lservrc (без расширения). Вот, например, демо-версия моей жертвы досталась мне с файлом следующего содержания (некоторая информация удалена по понятным причинам):Код (Text):
# XXX for: Quantum Sent with Order: YYY N2V3R7MXTCCQRM2VQGDSJLXEDJSR88N33SM4QZ# "BPR" version "0" 1Z26N2ZXJ5ANS95HMPGOEYX46HPSTB9Y7V274Z# "DRC" version "0" WA18DMVBR8NGDOXW9ZAUDLBAPXHNKORQPUWWET2FBA# "BPR" version "91" 5QM2OCB28W63R3XYIF473Z2PFY73XOTZ5O872JH5CY# "DRC" version "91"</pre> </blockquote> <p> Итого: 4 лицензии. Вся необходимая WlscGen'у информация любезно предоставлена в каждой строчке после '#'. А что если файла нет? Или он есть но без полной информации? Тогда чуть сложнее... Придётся задействовать IDA + SoftIce и перехватить вызовы LSRequest. Вот вам типичный пример использования LSRequest (фрагмент взят из первой попавшейся под руку демки): <blockquote><pre> 00463804 push eax 00463805 mov ecx, esi 00463807 mov BYTE PTR [3BCh+var_4], 0Bh 0046380F call sub_461810 00463814 mov ebp, [eax] 00463816 lea ecx, [3B8h+var_330] 0046381D push OFFSET unk_4E10B0 00463822 push ecx 00463823 mov BYTE PTR [3C0h+var_4], 0Ch 0046382B call sub_464090 00463830 mov eax, [eax] 00463832 push ebx <span class=y>; *handle</span> 00463833 push 0 <span class=y>; challenge</span> 00463835 lea edx, [3C8h+var_39C] 00463839 push 0 <span class=y>; log comment</span> 0046383B push edx <span class=y>; tokens</span> 0046383C push edi <span class=y>; feature version</span> 0046383D push ebp <span class=y>; feature name</span> 0046383E push eax <span class=y>; publisher</span> 0046383F push 0 <span class=y>; licsystem</span> 00463841 call _LSRequest 00463846 add esp, 28hПараметры LSRequest напоминают искомую нами информацию... Да, так оно и есть:
LSRequest(licsystem,publisher,feature name,feature version, tokens,log comment,challenge,*handle)
В вышеописанном фрагменте не фигурируют прямые указатели на строковые константы, передаваемые в LSRequest. Проще всего будет перехватить эти параметры в SoftIce. Ловим LSRequest примерно также как и computeVendorCode в первой главе. Потом извлекаем интересующие нас параметры из стека и радуемся жизни.
Теперь вы скажете: "А какого @#% мы столько возились с WlscGen, ведь можно было просто найти и пофиксить LSRequest в коде нашей жертвы?!". Хмм... Я предпочитаю оставить этот вопрос вам на засыпку. Помедитируйте над ним
Существуют и другие вариации SLM, как-то: сетевой сервер SLM, новомодный SentinelLM Shell (тоже входит в SDK). Но это уже темы для отдельных статей. Короче, бросайте всё и бегите добивать ваш таргет!
Неужели это всё?!
Наверное я не затронул и десятой доли того, что входит в теорию взлома SentinelLM. Вы дочитываете последнюю главу вступления в SLM! Если вы смогли удачно воплотить в код то, что я тут описал, плюс поняли суть самой защиты и её снятия, значит я не зря потратил время. Как говаривал Фокс Мольдер, "Я хочу верить"
Для дальнейшего изучения рекомендую кучу / уйму туториалов по взлому конкретных таргетов под защитой Sentinel. Особенно хороши статьи CyberHeg, CrackZ и Goatass, которые можно найти... в сети. Нет, всё это добро конечно лежит на сайте CrackZ, но сам сайт CrackZ постоянно кочует. Тут (http://66.98.132.48/krobar/collections/crackz.zip) можно качнуть оффлайновую версию всего сайта. На Krobars (http://krobars.reverse-engineering.info/) можно найти многие статьи по SLM и донглам вообще. Ещё эти статьи прозеркалены на других сайтах, даже в переводе. На испанском, например, это добро можно скачать с сайта Karpoff (http://hackindex.com/~karpoff/Manuales.htm#Dongles).
Теперь о главном...
Премного благодарствую всем писателям статей по Sentinel (too many и всех не перечислить), а также коллективу WASM.RU, включая уважаемых членов форума. Отдельной благодарности заслуживают volodya (щедрый поощритель начинающих писателей) и главные редакторы (вы их и сами знаете . Большое спасибо и вам, дорогой читатель и коллега. Да пребудет с вами сила Дзена и свобода мысли! © Quantum
SentinelLM!WlscGen_CRK_2
Дата публикации 10 сен 2003