allasm.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 осуществляются в стиле

 if(!isQueryOK()) goto fig_vam;

т.е. пропатчить этот код не просто, а очень просто! Причём RNBOsproQuery вызывается всего лишь 4 раза и обработка результата почти одинакова во всех четырёх случаях. Итак, грузим WlscGen.exe в IDA, применяем соответствующие сигнатуры и задаём поиск вызовов RNBOsproQuery. Вот вам и первый результат:

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     ; Генерируем случайное число (X)
0042362A             cdq
0042362B             mov    ecx, 0Ah
00423630             idiv   ecx       ; dx = X%10 (остаток от деления)
00423632             movzx  eax, dx
00423635             imul   eax, 9
00423638             mov    [var_A], dx   ; Сохраняем случайное число
                     . . .
00423659             lea    ecx, [var_18]
0042365C             lea    eax, [var_94]
00423662             lea    edx, [var_D0]
00423668             push   4                    ; length
0042366A             push   ecx                  ; *response32
0042366B             push   eax                  ; *response
0042366C             lea    ecx, [var_4D4]
00423672             push   edx                  ; *queryData
00423673             push   0Ch                  ; address
00423675             push   ecx                  ; packet
00423676             call   _RNBOsproQuery@24
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   _strncmp              ; Оно или не оно?
004236A8             add    esp, 0Ch
004236AB             mov    edi, eax
004236AD             test   edi, edi
004236AF             jz     short loc_4236C4      ; Оно!!!

Что делает этот код? Ладно, объясняю:

  1. генерируем случайное число стандартным сишным методом;
  2. колдуем над этим числом и переводим его в массив;
  3. подсовываем этот массив в RNBOsproQuery для обработки алгоритмом 0Ch (адрес начальной ячейки алгоритма);
  4. результат обработки конвертируем в ASCII-строку;
  5. используем случайное число из пункта 1 для адресации фиксированного в экзешнике массива со строковыми константами;
  6. сравниваем обе строки;
  7. если строки совпадают, продолжаем (последний переход JZ).

Тот фиксированный массив в программе является своеобразным рангом алгоритма 0Ch, т.е. он хранит все возможные результаты данного алгоритма. Я рекомендую вам пройтись по данному участку кода под отладчиком и убедиться в тривиальности всей проблемы. Что нам мешает исправить тот последний переход? Ну, вообще говоря, правильнее будет поксорить регистр EAX на месте вызова _strncmp, как будто строки равны. Заодно можно убрать вызов RNBOsproQuery чтоб лишний раз не беспокоить драйвер. Тривиальный патч:

00423668             push   4
0042366A             push   ecx
0042366B             push   eax
0042366C             lea    ecx, [var_4D4]
00423672             push   edx
00423673             push   0Ch
00423675             push   ecx
00423676 83C418      add    esp, 18h    ; Восстанавливаем стек
00423679 90          nop                ; вместо вызова RNBOsproQuery
00423679 90          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 33С0        xor    eax, eax     ; обнуляем EAX
004236A5 90          nop                 ; вместо вызова strncmp
004236A6 90          nop
004236A7 90          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 (без расширения). Вот, например, демо-версия моей жертвы досталась мне с файлом следующего содержания (некоторая информация удалена по понятным причинам):
 # XXX for: Quantum Sent with Order: YYY
 N2V3R7MXTCCQRM2VQGDSJLXEDJSR88N33SM4QZ# "BPR" version "0"
 1Z26N2ZXJ5ANS95HMPGOEYX46HPSTB9Y7V274Z# "DRC" version "0"
 WA18DMVBR8NGDOXW9ZAUDLBAPXHNKORQPUWWET2FBA# "BPR" version "91"
 5QM2OCB28W63R3XYIF473Z2PFY73XOTZ5O872JH5CY# "DRC" version "91"

Итого: 4 лицензии. Вся необходимая WlscGen'у информация любезно предоставлена в каждой строчке после '#'. А что если файла нет? Или он есть но без полной информации? Тогда чуть сложнее... Придётся задействовать IDA + SoftIce и перехватить вызовы LSRequest. Вот вам типичный пример использования LSRequest (фрагмент взят из первой попавшейся под руку демки):

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                  ; *handle
00463833             push   0                    ; challenge
00463835             lea    edx, [3C8h+var_39C]
00463839             push   0                    ; log comment
0046383B             push   edx                  ; tokens
0046383C             push   edi                  ; feature version
0046383D             push   ebp                  ; feature name
0046383E             push   eax                  ; publisher
0046383F             push   0                    ; licsystem
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 (щедрый поощритель начинающих писателей) и главные редакторы (вы их и сами знаете :-). Большое спасибо и вам, дорогой читатель и коллега. Да пребудет с вами сила Дзена и свобода мысли!

  [C] Quantum