Глава 30
Last updated
Was this helpful?
Last updated
Was this helpful?
(для запуска крэкми понадобиться MSVBVM50.dll )
Ок, после того, как попрактиковались, и если вы ещё живы, продолжаем с P-CODE.
У нас есть ещё кое-какие опкоды, собранные из туториалов JBDUC’а.
6c -> ILdRf Поместить адрес в стек
1b -> LitStr5 Поместить строку в стек
fb -> Lead0 Сравнить две строки (хе-хе, для чего это могло служить ?)
30 -> EqStr Сравнить две строки(хе-хе, для чего это могло служить ?)
2f -> FFree1Str Освободить используемую память
1a -> FFree1Ad Освободить используемую память
0f -> VCallAd Выполнить опкод в виртуальной машине
1c -> BranchF Сделать переход, если предшествующее сравнение дало ложь ( аналог jne/jnz в ассемблере )
1d -> BranchT Сделать переход, если предшествующее сравнение дало истину ( аналог je/jz в ассемблере )
1e -> Безусловный переход ( хе-хе, угадайте, для чего её можно использовать )
fc -> Lead1 Прерывает выполнение программы (хе-хе, прекрасно...)
c8 -> End Прерывает выполнение программы (хе-хе, прекрасно...)
f3 -> LitI2 Сохраняет заданное число типа Integer в стек
f4 -> LitI2_Byte Конвертирует Byte-значение в Integer и помещает его в стек
70 -> FStI2 Сохраняет последний Integer, помещённый в стек, в заданную глобальную переменную
6b -> FLdI2 Загружает в стек Integer из заданной локальной переменной
a9 -> AddI2 Складывает два последних Integer'а, помещённых в стек, на вершину которого помещает результат
ad -> SubI2 Использует для вычитания два последних Integer’а, помещённых в стек, и кладёт в последний результат
b1 -> MulI2 Умножает два последних Integer’а, помещённых в стек, и кладёт в последний результат. Думаю, что если переполнение игнорируется.
Вот он, и видим, что начинается с 401cc0. Не будем слепо верить EXDEC’у, так как бывали случаи, когда он обманывал. Посмотрим, сможем ли мы найти первый опкод вручную, как мы это делали в предыдущей части.
Смотрим, находится ли переход на API-функцию MethCallEngine за EP.
Вот он. Становимся на него, делаем FOLLOW, чтобы перейти к API-функции и также устанавливаем там BP.
Готово, теперь делаем RUN, чтобы остановиться на одном из установленных BP.
Окно появляется до того, как сработает останов, дело в том, что окно появляется до того, как начинается P-CODE, хотя нужно пояснить, что такое бывает не всегда, может сначала сработать останов, а потом появиться окно, но в данном случае оно сделало это первым. Вводим имя и неправильный серийный номер.
Теперь жмём RUN, пока не встретится знакомая инструкция, где читается опкод.
Вот тут, помним, что ESI всегда должен указывать на опкод (который сам перемещается в AL).
Как видим, EXDEC не ошибся, и первый опкод – это 04 и начинается с 401cc0.
Помним, что 04 – это просто PUSH, в данном случае его аргументом является EBP-8C, как это видно из пояснения рядом с опкодом
04 567B 0B8E 2 1 2 Push arg
Из этого нам становится понятно, что каждая цифра, в нашем случае 0B8E, является RVA опкода, 2 – это количество байтов, которые в общей сложности занимают параметры, 1 нам говорит, что аргумент только один, и последнее значение (2) указывает нам размер, занимаемый каждым аргументом в отдельности.
Хорошо, вот PUSH EBP-8C, продолжаем изучать исходник, пока без трассировки шаг за шагом. Смотрим.
Видим, что используется два вызова для считывания введённого в окне регистрации, вероятно, что в первый раз читает имя, а во второй – неправильный серийник, использованный нами. Устанавливаем BPM ON ACCESS на первом вызове, то есть на 401d4C.
Вот опкод, устанавливаем на него BPM и делаем RUN.
Ок, теперь трассируем до следующего опкода, чтобы посмотреть, что будет происходить с именем.
Дошли до следующего опкода, смотрим стек, чтобы узнать, что там находится
Как видим, продолжается работа с локальной переменной 8c (или EBP-8C). Проверим, что там сохранено. На моей машине в ebp-8c содержит 12f454.
Ищем этот адрес в стеке и видим, что там находится моё имя.
Конечно же, оно находится здесь, так что всё идёт хорошо. Раз здесь находится моё имя, то логично предположить, что в следующем будет идти работа с серийником, поэтому устанавливаем на второй опкод BPM ON ACCESS.
401DFB: 0d VCallHresult get__ipropTEXTEDIT
Готово, теперь жмём RUN, чтобы оказаться рядом с этим опкодом.
Как и в предыдущем случае, трассируем до следующего опкода.
И смотрим, сохранён ли в используемой переменной неправильный серийный номер.
Как и в прошлый раз используется EBP-8C.
Хорошо, вот мы и дошли до места, куда помещён наш неправильный серийник. Видим, что зная опкоды, не обязательно трассировать всю программу, можем просто установить BPM и оказаться в нужной части программы. В прошлой главе мы трассировали всё подряд, чтобы понять механизмы, согласно которым работает P-CODE, но как правильно, нам не нужно делать это. Как мы увидим в следующей главе, когда исследуем большую программу, будем локализовывать интересующую нас часть программы и работать именно с ней.
Далее видим, что здесь есть все признаки сравнения, потом освобождение локальных переменных с помощью FREE, а затем условный переход, который ведёт в 401e59, где вызывается rtcMsgBox с сообщением о правильном серийном номере, а если перехода не происходит, то отображается сообщение о том, что серийник неверен.
Тут всё совершенно понятно. Вероятное сравнение и условный переход, в зависимости от которого отображается окошко с соответствующим сообщением, так что устанавливаем сюда BPM ON ACCESS.
Ок, делаем RUN.
Останавливаемся тут, в первом опкоде и трассируем до второго, который производит некую операцию.
Второй опкод равен EF.
Смотрим в списке опкодов.
Что такое FB EF нам ещё не совсем понятно, EXDEC говорит нам что-то о конкатенации переменных, смотрим.
Видим, что прямо до этого опкода идёт работа с двумя локальными переменными, в одной из которых находится строка CRK, а другая является EBP-018C. Выясним, что находится в каждой из вышеупомянутых переменных.
Первая переменная – это EBP-9C, которая на моей машине равна 12f444 и находится там следующее:
Как мы знаем, в случае с переменными сначала идёт байт, указывающий тип, в данном случае это 3, а ещё выше находится сама переменная, которая равна 2EA.
Входим в опкод.
Здесь читаются параметры.
После сложения с EBP получается 12f434.
Доходим до API-функции vbaVarCat, у которой три аргумента в стеке.
Смотрим, что находится в каждом из них. Отметим, что когда идёт работа с переменной, то отображается маленькая структура, в которой первый байт задаёт её тип.
Это первый аргумент.
Во втором сначала идёт 3, а потом 02EA, задающее значение.
А в третьем в начале 8, означающее, что 4017648 – это указатель на строку CRK.
Ок, у нас тут мешанина из переменных, посмотрим, что останется. Если войдём в функцию vbaVarCat, то окажемся у внутренней API-функции, которая ясно покажет нам, что будет объединяться.
То есть предстоит объединение CRK с 746. А теперь выясним, что значит параметр 02EA.
Он конвертируется в строку. 02EA – это:
То есть vbaVarCat, в данном случае, получила строку и численную переменную, которая конвертируется в строку, после чего обе объединяются.
Продолжаем трассировать с помощью F8.
Дойдя до RET’а из функции, видим:
Обе переменных объединились в одну строку.
И, как и раньше, до начала следующего опкода это значение сохраняется в качестве первого аргумента.
Видим, что первый аргумент сейчас имеет тип 8, то есть строку, и указывает на 15d88c, то есть на сконкатенированную строку.
Ок, теперь снова должно произойти сравнение, быстро доходим до следующего опкода.
401E13: Lead0/40 NeVarBool
Это FB40, и он двойной. Трассируем, пока не дойдём до считывания второго опкода.
Конечно, список опкодов от Микрософта ничего нам о нём не говорит, так что трассируем опкод, чтобы узнать, что он делает.
Видим, что в опкоде всего один вызов, после которого сразу следует завершение. Смотрим параметры вызова.
Первый равен нулю, а второй 12f434. Смотрим, что там находится.
Ок, 08 говорит нам, что это строка, теперь осталось посмотреть, на что указывает 15d88c.
Вот строка, смотрим следующий аргумент.
В данном случае 15ca94 указывает на строку с нашим неправильным серийным номером.
Похоже, что это сравнение двух строк.
Чтобы рассеять сомнения, устанавливаем BP, минуем вызов с помощью F8 и идём к следующему опкоду.
Видим, что в стеке осталось значение FFFFFFFF, вероятно, означающее, что строки не равны. Запишем возможный серийный номер, и попробуем его использовать.
Нажимаем на кнопку "Registrar".
Снова оказались у этого вызова, проходим его с помощью F8 и идём к следующему опкоду, как в прошлый раз.
Видим, что в данном случае результатом является ноль, так как обе строки одинаковы.
Как мы увидели, не обязательно трассировать всё подряд. Нужно найти подозрительную область и трассировать только неизвестные опкоды. Этого оказалось достаточным, чтобы найти серийный номер исследуемой программы.
Для заинтересованных – более детализированный список, где изложены сведения, которые нам удалось узнать.
Отсюда ясно, как работает крэкми.
Полезно знать, как работать с P-CODE в OllyDbg, так как есть программы, которые защищены от WKT и EXDEC, но в случае с OllyDbg мы можем использовать плагины, которые спрячут её практически ото всех, кроме очень редких исключений.
Видим, что знаменитый NAG – это просто rtcMsgBox. В P-Code нет NOP’ов, хе-хе, поэтому нам нужно забить эту функцию опкодами, которые не изменят хода выполнения программы.
Устанавливаем BPM на опкоде, вызывающем rtcMsgBox, и запускаем программу.
401A2D: 0a ImpAdCallFPR4: _rtcMsgBox
Останавливаемся, когда считывается опкод.
Видим, что в стеке находится 12f9e8. Идём к следующему опкоду. Сначала, конечно, выскочит наг-окошко, поэтому надо будет нажать "Aceptar".
Здесь мы доходим до следующего опкода после CALL EAX, который вызывал API-функцию rtcMsgBox.
В стеке находится 12f9fc.
То есть, для того, чтобы оставить равные, нужно сделать различные POP’ы. Сюда мы не вмешиваемся, можем попробовать использовать PUSH с помощью F5.
F5 5CBE 1377 4 1 4 Push imm#4
У нас 4 параметра размером равные 0A. Столько нам нужно заменить.
0A 664E 1F30 4 2 2 2
Пробуем изменить 0A на F5 и делаем все параметры равными нулю.
Следующий опкод – это 36, как показывает нам EXDEC. Мы всегда должны быть уверенными, что замещающий опкод имеет такое же количество параметров, как и у замещаемого, чтобы не было проблем. Сохраняем изменения.
31 часть будет последней посвящённой P-CODE, и в ней будет рассмотрена коммерческая программа.
[C] Рикардо Нарваха, пер. Aquila
Ок, есть ещё опкоды, которые мы сейчас рассматривать не будем. К статье прилагает файл под названием "P-CODE OPCODES" , который, предположительно, распространялся Микрософтом. Он может немного помочь, так как в нём перечислены опкоды и что они делают (но не все, хе-хе), так что, если встретим незнакомый опкод, то можем посмотреть, есть ли там описание.
Как я и обещал вам, сначала рассмотрим крэкми "clave 2" , который остался у нас в качестве домашнего упражнения с прошлого раза. Посмотрим, можем ли мы получить листинг с помощью EXDEC’а .
После нажатия на "REGISTRAR" срабатывает останов на JMP. Идём в первую секцию (помните, что не надо использовать пропатченный OllyDbg для OEP’ов ) и устанавливаем "BPM ON ACCESS" на секцию кода.
Загружаем прилагающийся крэкми nags1 , который просит нас убрать первоначальное наг-окно. Смотрим его листинг в EXDEC.
Доходим досюда без появления наг-окна. В другой раз, вероятно, можно использовать иной опкод для патчинга. В качестве домашнего упражнения вы можете попробовать решить крэкми nags2 , прилагающийся к данной статье.