📌
Введение в крэкинг с помощью OllyDbg
  • Initial page
  • TODO
  • Указатель
  • Глава 1
  • Глава 2
  • Глава 3
  • Глава 4
  • Глава 5
  • Глава 6
  • Глава 7
  • Глава 8
  • Глава 9
  • Глава 10
  • Глава 11
  • Глава 12
  • Глава 13
  • Глава 14
  • Глава 15
  • Глава 16
  • Глава 17
  • Глава 18
  • Глава 19
  • Глава 20
  • Глава 21
  • Глава 22
  • Глава 23
  • Глава 24
  • Глава 25
  • Глава 26
  • Глава 27
  • Глава 28
  • Глава 29
  • Глава 30
  • Глава 31
  • Глава 32
  • Глава 33
  • Глава 34
  • Глава 35
  • Глава 36
  • Глава 37
  • Глава 38
  • Глава 39
  • Глава 40
  • Глава 41
  • Глава 42
  • Глава 43
  • Глава 44
  • Глава 45
  • Глава 46
  • Глава 47
  • Глава 48
  • Глава 49
  • Глава 50
  • Глава 51
  • Глава 52
  • Глава 53
  • Глава 54
  • Глава 55
  • Глава 56
  • Глава 57
  • Глава 58
Powered by GitBook
On this page

Was this helpful?

Edit on GitHub

Глава 30

PreviousГлава 29NextГлава 31

Last updated 2 years ago

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.

Proc: 401e90
401CC0: 04 FLdRfVar                local_008C

Помним, что 04 – это просто PUSH, в данном случае его аргументом является EBP-8C, как это видно из пояснения рядом с опкодом

04 567B 0B8E 2 1 2 Push arg

Из этого нам становится понятно, что каждая цифра, в нашем случае 0B8E, является RVA опкода, 2 – это количество байтов, которые в общей сложности занимают параметры, 1 нам говорит, что аргумент только один, и последнее значение (2) указывает нам размер, занимаемый каждым аргументом в отдельности.

Хорошо, вот PUSH EBP-8C, продолжаем изучать исходник, пока без трассировки шаг за шагом. Смотрим.

401D4C: 0d VCallHresult            get__ipropTEXTEDIT

401DFB: 0d VCallHresult            get__ipropTEXTEDIT

Видим, что используется два вызова для считывания введённого в окне регистрации, вероятно, что в первый раз читает имя, а во второй – неправильный серийник, использованный нами. Устанавливаем BPM ON ACCESS на первом вызове, то есть на 401d4C.

Вот опкод, устанавливаем на него BPM и делаем RUN.

Ок, теперь трассируем до следующего опкода, чтобы посмотреть, что будет происходить с именем.

Дошли до следующего опкода, смотрим стек, чтобы узнать, что там находится

401D4C: 0d VCallHresult            get__ipropTEXTEDIT
401D51: 3e FLdZeroAd               local_008C

Как видим, продолжается работа с локальной переменной 8c (или EBP-8C). Проверим, что там сохранено. На моей машине в ebp-8c содержит 12f454.

Ищем этот адрес в стеке и видим, что там находится моё имя.

Конечно же, оно находится здесь, так что всё идёт хорошо. Раз здесь находится моё имя, то логично предположить, что в следующем будет идти работа с серийником, поэтому устанавливаем на второй опкод BPM ON ACCESS.

401DFB: 0d VCallHresult get__ipropTEXTEDIT

Готово, теперь жмём RUN, чтобы оказаться рядом с этим опкодом.

Как и в предыдущем случае, трассируем до следующего опкода.

И смотрим, сохранён ли в используемой переменной неправильный серийный номер.

401DFB: 0d VCallHresult            get__ipropTEXTEDIT
401E00: 3e FLdZeroAd               local_008C

Как и в прошлый раз используется EBP-8C.

Хорошо, вот мы и дошли до места, куда помещён наш неправильный серийник. Видим, что зная опкоды, не обязательно трассировать всю программу, можем просто установить BPM и оказаться в нужной части программы. В прошлой главе мы трассировали всё подряд, чтобы понять механизмы, согласно которым работает P-CODE, но как правильно, нам не нужно делать это. Как мы увидим в следующей главе, когда исследуем большую программу, будем локализовывать интересующую нас часть программы и работать именно с ней.

401E0F: Lead0/ef ConcatVar
401E13: Lead0/40 NeVarBool
401E15: 1a FFree1Ad                local_0088
401E18: 36 FFreeVar         local_00CC local_00AC
401E1F: 1c BranchF:                401E59

Далее видим, что здесь есть все признаки сравнения, потом освобождение локальных переменных с помощью FREE, а затем условный переход, который ведёт в 401e59, где вызывается rtcMsgBox с сообщением о правильном серийном номере, а если перехода не происходит, то отображается сообщение о том, что серийник неверен.

Тут всё совершенно понятно. Вероятное сравнение и условный переход, в зависимости от которого отображается окошко с соответствующим сообщением, так что устанавливаем сюда BPM ON ACCESS.

401E0F: Lead0/ef ConcatVar
401E13: Lead0/40 NeVarBool

Ок, делаем RUN.

Останавливаемся тут, в первом опкоде и трассируем до второго, который производит некую операцию.

Второй опкод равен EF.

Смотрим в списке опкодов.

FB EF 6BAB 25AD 2
FB F0 6B99 259B 0          vbaStrCat
FB F1 C423 B981 0          Push [FC0D134]

Что такое FB EF нам ещё не совсем понятно, EXDEC говорит нам что-то о конкатенации переменных, смотрим.

401E07: 3a LitVarStr:              ( local_009C ) CRK
401E0C: 04 FLdRfVar                local_018C
401E0F: Lead0/ef ConcatVar
401E13: Lead0/40 NeVarBool

Видим, что прямо до этого опкода идёт работа с двумя локальными переменными, в одной из которых находится строка 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 и идём к следующему опкоду, как в прошлый раз.

Видим, что в данном случае результатом является ноль, так как обе строки одинаковы.

Как мы увидели, не обязательно трассировать всё подряд. Нужно найти подозрительную область и трассировать только неизвестные опкоды. Этого оказалось достаточным, чтобы найти серийный номер исследуемой программы.

Для заинтересованных – более детализированный список, где изложены сведения, которые нам удалось узнать.

401CC0: 04 FLdRfVar                local_008C
401CC3: 21 FLdPrThis              **; Загрузка ссылочного указателя в указатель на элемент**
401CC4: 0f VCallAd                 text			; Доступ к методу **ITEM DESCRIPTOR TABLE**
401CC7: 19 FStAdFunc               local_0088
401CCA: 08 FLdPr                   local_0088
401CCD: 0d VCallHresult            get__ipropTEXTEDIT  ; Читаем содержимое из текстового поля
401CD2: 6c ILdRf                   local_008C  ; Имя
401CD5: 1b LitStr:                 &  ; Поместить строку в стек
401CD8: Lead0/30 EqStr            	  ; Сравнить две строки
401CDA: 2f FFree1Str               local_008C
401CDD: 1a FFree1Ad                local_0088
401CE0: 1c BranchF:                401CE6  ; Переход, если сравнение не удалось
401CE3: 1e Branch:                 401e8c  ; Безусловный переход
401CE6: 04 FLdRfVar                local_008C
401CE9: 21 FLdPrThis
401CEA: 0f VCallAd                 text  ; Доступ к методу **ITEM DESCRIPTOR TABLE**
401CED: 19 FStAdFunc               local_0088
401CF0: 08 FLdPr                   local_0088
401CF3: 0d VCallHresult            get__ipropTEXTEDIT  ; Читаем содержимое текстового поля
401CF8: 6c ILdRf                   local_008C          ; Имя
401CFB: 4a FnLenStr
401CFC: f5 LitI4:                  0x6  6  (....)  ; Передаём элемент из 4-ёх байт
401D01: d1 LtI4                   			       ; Сравнение меньше чем (?)
401D02: 2f FFree1Str               local_008C
401D05: 1a FFree1Ad                local_0088
401D08: 1c BranchF:                401D3F  ; Переход если ложно (>= 6)
401D0B: 27 LitVar_Missing
401D0E: 27 LitVar_Missing
401D11: 3a LitVarStr:              ( local_00BC ) P-Code
401D16: 4e FStVarCopyObj           local_00CC
401D19: 04 FLdRfVar                local_00CC
401D1C: f5 LitI4:                  0x40  64  (...@)
401D21: 3a LitVarStr:              ( local_009C ) Минимум 6 символов
401D26: 4e FStVarCopyObj           local_00AC
401D29: 04 FLdRfVar                local_00AC
401D2C: 0a ImpAdCallFPR4:          _rtcMsgBox
401D31: 36 FFreeVar                local_00AC local_00CC local_00EC local_010C
401D3C: 1e Branch:                 401e8c  ; Если < 6 символов в строке
401D3F: 04 FLdRfVar                local_008C
401D42: 21 FLdPrThis
401D43: 0f VCallAd                 text
401D46: 19 FStAdFunc               local_0088
401D49: 08 FLdPr                   local_0088
401D4C: 0d VCallHresult            get__ipropTEXTEDIT  ; Читаем содержимое текстового поля
401D51: 3e FLdZeroAd               local_008C		   ; Имя
401D54: 46 CVarStr                 local_00AC
401D57: 04 FLdRfVar                local_00CC
401D5A: 0a ImpAdCallFPR4:          _rtcLowerCaseVar  ; Конвертируем в строчные символы
401D5F: 04 FLdRfVar                local_00CC
401D62: 04 FLdRfVar                local_00EC
401D65: 0a ImpAdCallFPR4:          _rtcTrimVar
401D6A: 04 FLdRfVar                local_00EC
401D6D: Lead1/f6 FStVar            local_011C
401D71: 1a FFree1Ad                local_0088
401D74: 36 FFreeVar                local_00AC local_00CC
401D7B: 04 FLdRfVar                local_011C
401D7E: Lead0/eb FnLenVar
401D82: Lead1/f6 FStVar            local_012C
401D86: 28 LitVarI2:               ( local_00BC ) 0x1  (1)
401D8B: 04 FLdRfVar                local_013C
401D8E: 04 FLdRfVar                local_012C
401D91: Lead3/68 ForVar:           (when done) 401DE0		, Начало цикла for next
401D97: 28 LitVarI2:               ( local_00AC ) 0x1  (1)
401D9C: 04 FLdRfVar                local_013C
401D9F: Lead1/22 CI4Var
401DA1: 04 FLdRfVar                local_011C
401DA4: 04 FLdRfVar                local_00CC
401DA7: 0a ImpAdCallFPR4:          _rtcMidCharVar  ; Загрузка символов из...
401DAC: 04 FLdRfVar                local_00CC	   ; ... имени
401DAF: Lead2/fe CStrVarVal        local_008C
401DB3: 0b ImpAdCallI2             _rtcAnsiValueBstr  **; конвертируем значения символов в  hexadec**
401DB8: 44 CVarI2                  local_00BC
401DBB: Lead1/f6 FstVar		       local_016C
401DBF: 2f FFree1Str        	   local_008C
401DC2: 36 FFreeVar                local_00AC local_00CC
401DC9: 04 FLdRfVar                local_017C
401DCC: 04 FLdRfVar                local_016C
401DCF: Lead0/94 AddVar            local_00AC
401DD3: Lead1/f6 FStVar            local_017C
401DD7: 04 FLdRfVar                local_013C
401DDA: Lead3/7e NextStepVar:      (continue) 401D97  ; Конец цикла for-next?
401DE0: 04 FLdRfVar                local_017C
401DE3: 04 FLdRfVar                local_012C
401DE6: Lead0/94 AddVar            local_00AC
401DEA: Lead1/f6 FStVar            local_018C
401DEE: 04 FLdRfVar                local_008C
401DF1: 21 FLdPrThis
401DF2: 0f VCallAd                 text
401DF5: 19 FStAdFunc               local_0088
401DF8: 08 FLdPr                   local_0088
401DFB: 0d VCallHresult            get__ipropTEXTEDIT  ; Читаем содержимое текстового поля
401E00: 3e FLdZeroAd               local_008C		   ; Серийный номер
401E03: 46 CVarStr                 local_00CC
401E06: 5d HardType
401E07: 3a LitVarStr:              ( local_009C ) CRK
401E0C: 04 FLdRfVar                local_018C
401E0F: Lead0/ef ConcatVar
401E13: Lead0/40 NeVarBool
401E15: 1a FFree1Ad                local_0088
401E18: 36 FFreeVar                local_00CC local_00AC
401E1F: 1c BranchF:                401E59
401E22: 27 LitVar_Missing
401E25: 27 LitVar_Missing
401E28: 3a LitVarStr:              ( local_00BC ) P-Code
401E2D: 4e FStVarCopyObj           local_00CC
401E30: 04 FLdRfVar                local_00CC
401E33: f5 LitI4:                  0x10  16  (....)
401E38: 3a LitVarStr:              ( local_009C ) Серийный номер неверен!
401E3D: 4e FStVarCopyObj           local_00AC
401E40: 04 FLdRfVar                local_00AC
401E43: 0a ImpAdCallFPR4:          _rtcMsgBox
401E48: 36 FFreeVar                local_00AC local_00CC local_00EC local_010C
401E53: 1e Branch:                 401e8c
401E56: 1e Branch:                 401e8c
401E59: 27 LitVar_Missing
401E5C: 27 LitVar_Missing
401E5F: 3a LitVarStr:              ( local_00BC ) P-Code
401E64: 4e FStVarCopyObj           local_00CC
401E67: 04 FLdRfVar                local_00CC
401E6A: f5 LitI4:                  0x30  48  (...0)
401E6F: 3a LitVarStr:              ( local_009C ) Серийный номер верен!!
401E74: 4e FStVarCopyObj           local_00AC
401E77: 04 FLdRfVar                local_00AC
401E7A: 0a ImpAdCallFPR4:          _rtcMsgBox
401E7F: 36 FFreeVar                local_00AC local_00CC local_00EC local_010C
401E8A: Lead1/c8 End
401E8C: 13 ExitProcHresult

Отсюда ясно, как работает крэкми.

Полезно знать, как работать с P-CODE в OllyDbg, так как есть программы, которые защищены от WKT и EXDEC, но в случае с OllyDbg мы можем использовать плагины, которые спрячут её практически ото всех, кроме очень редких исключений.

;Proc: 401a40

401A14: 27 LitVar_Missing
401A17: 27 LitVar_Missing
401A1A: 27 LitVar_Missing
401A1D: f5 LitI4:                  0x0  0  (....)
401A22: 3a LitVarStr:              ( local_0094 ) NAG
401A27: 4e FStVarCopyObj           local_00A4
401A2A: 04 FLdRfVar                local_00A4
401A2D: 0a ImpAdCallFPR4:          _rtcMsgBox
401A32: 36 FFreeVar         local_00A4 local_00C4 local_00E4 local_0104
401A3D: 13 ExitProcHresult
;Proc: 401958

401954: Lead1/c8 End
401956: 13 ExitProcHresult**

Видим, что знаменитый 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 , прилагающийся к данной статье.

[ссылка]
[ссылка]
[ссылка]
[ссылка]
[ссылка]
[ссылка]
[ссылка]