Ограничения Демоверсии

Тестирую сейчас демоверсию, в частности посмотрел что из себя представляет морф кода без виртуализации. В самом простейшем примере получилось что добавляются всего лишь мусорные инструкции:

.vmp2:0056FCBD sub_56FCBD      proc near               ; CODE XREF: sub_418860j
.vmp2:0056FCBD                 push    ebp
.vmp2:0056FCBE                 cwd                     ; мусор
.vmp2:0056FCC0                 movsx   dx, bh          ; мусор
.vmp2:0056FCC4                 mov     ebp, esp
.vmp2:0056FCC6                 xchg    eax, edx        ; мусор
.vmp2:0056FCC7                 not     edx             ; мусор
.vmp2:0056FCC9                 mov     eax, off_41D628
.vmp2:0056FCCE                 xchg    dh, dl          ; мусор
.vmp2:0056FCD0                 cmovns  dx, di          ; мусор
.vmp2:0056FCD4                 mov     edx, offset aMorphedCode ; "Morphed code"
.vmp2:0056FCD9                 call    sub_4050F0
.vmp2:0056FCDE                 call    sub_4051F0
.vmp2:0056FCE3                 call    sub_404004
.vmp2:0056FCE8                 pop     ebp
.vmp2:0056FCE9                 retn
.vmp2:0056FCE9 sub_56FCBD      endp

Вопрос, в полной версии результаты морфа останутся такими же или это просто сама демонстрация как именно происходит морф?

Ну и тоже по виртуальной машине. этот-же код накрытый через MAP файл:

procedure Foo0;
begin
  Writeln('ххх');
end;

виртуализация: всего 38 уникальных обработчиков, общее число циклов VM 435
виртуализация + мутация: всего 94 уникальных обработчика, общее число циклов VM 1001
виртуализация + мутация + привязка к серийному номеру: всего 107 уникальных обработчика, общее число циклов VM 14825

Маловато будет циклов, или это действительно просто ограничение демоверсии (один два прохода глубина виртуализации и обфускации)?

Все становится интересней.

Сейчас попросил покрыть тот-же проект у человека с легальной VMProt 3.0.5.

Мутация вообще не произведена:

Т.е все оставлено в том виде как и было

CPU Disasm
Address   Hex dump          Command                                  Comments
008AA311    55              PUSH EBP
008AA312    8BEC            MOV EBP,ESP
008AA314    A1 C0D74D00     MOV EAX,DWORD PTR DS:[4DD7C0]
008AA319    BA 30534D00     MOV EDX,004D5330                         ; UNICODE "Morphed code"
008AA31E    E8 C5DCB5FF     CALL 00407FE8
008AA323    E8 00DFB5FF     CALL 00408228
008AA328    E8 CFC4B5FF     CALL 004067FC
008AA32D    5D              POP EBP
008AA32E    C3              RETN

Это как так?

Ладно смотрю VM, общего цикла нет, уже хорошо. Инструкции пикода с ESI забираются непосредственно в хэндлерах, но…

Рассмотрим хэндлер:

00899985    0FB606          MOVZX EAX,BYTE PTR [ESI]
00899988    81C6 01000000   ADD ESI,1
0089998E    33CC            XOR ECX,ESP
00899990    03CF            ADD ECX,EDI
00899992    32C3            XOR AL,BL
00899994    66:8BCF         MOV CX,DI
00899997    0F98C5          SETS CH
0089999A    FEC0            INC AL
0089999C    80D1 F5         ADC CL,0F5
0089999F    F6D8            NEG AL
008999A1    0FBAF9 F1       BTC ECX,0F1                                                            ; Shift out of range
008999A5    3AD4            CMP DL,AH
008999A7    66:81C9 0363    OR CX,6303
008999AC    34 65           XOR AL,65
008999AE    0FC9            BSWAP ECX
008999B0    66:81C1 8465    ADD CX,6584
008999B5    66:81E1 1559    AND CX,5915
008999BA    2C 96           SUB AL,96
008999BC    0FBFC9          MOVSX ECX,CX
008999BF    66:0FB6CF       MOVZX CX,BH
008999C3    66:81E9 2B4F    SUB CX,4F2B
008999C8    32D8            XOR BL,AL
008999CA    F9              STC
008999CB    0FC9            BSWAP ECX
008999CD    8B4C25 00       MOV ECX,DWORD PTR [EBP]
008999D1    F5              CMC
008999D2    81C5 04000000   ADD EBP,4
008999D8    F8              CLC
008999D9    890C04          MOV DWORD PTR [EAX+ESP],ECX
008999DC    C0C4 7D         ROL AH,7D                                                              ; Shift out of range
008999DF    66:F7D8         NEG AX
008999E2    66:81FE BD10    CMP SI,10BD
008999E7    8B06            MOV EAX,DWORD PTR [ESI]
008999E9    66:85C6         TEST SI,AX
008999EC    81C6 04000000   ADD ESI,4
008999F2    85D8            TEST EAX,EBX
008999F4    33C3            XOR EAX,EBX
008999F6    F7D8            NEG EAX
008999F8    F7D0            NOT EAX
008999FA    40              INC EAX
008999FB    85C3            TEST EBX,EAX
008999FD    F7D8            NEG EAX
008999FF    F5              CMC
00899A00    F8              CLC
00899A01    33D8            XOR EBX,EAX
00899A03    03F8            ADD EDI,EAX
00899A05    57              PUSH EDI
00899A06    C3              RET

Вот мне страшно на такое смотреть ибо это даже руками чистится на глазок и превращается вот в такое:

00899985    0FB606          MOVZX EAX,BYTE PTR [ESI]
00899988    81C6 01000000   ADD ESI,1
008999CD    8B4C25 00       MOV ECX,DWORD PTR [EBP]

00899992    32C3            XOR AL,BL
0089999A    FEC0            INC AL
008999AC    34 65           XOR AL,65
008999BA    2C 96           SUB AL,96
008999C8    32D8            XOR BL,AL

008999D2    81C5 04000000   ADD EBP,4
008999D9    890C04          MOV DWORD PTR [EAX+ESP],ECX

008999E7    8B06            MOV EAX,DWORD PTR [ESI]

008999EC    81C6 04000000   ADD ESI,4

008999F4    33C3            XOR EAX,EBX
008999F6    F7D8            NEG EAX
008999F8    F7D0            NOT EAX
008999FA    40              INC EAX
008999FD    F7D8            NEG EAX

00899A01    33D8            XOR EBX,EAX
00899A03    03F8            ADD EDI,EAX
00899A05    57              PUSH EDI
00899A06    C3              RET

А где обфускация?

Мы уже подали заявку на покупку вашего ПО, поэтому разъясните данные вопросы.

Еще, забыл момент.
Демоприложение собиралось со всеми отключенными опциями (защита памяти etc) проверялась только работа самого механизма защиты.
Под виртуалкой (ХР2 32 в среде от Парралелей) приложение закрывается сразу после появления сообщения о том, что защищено демонстрационной версией. (в отладчике умираем со статусом DEADCODE).
При этом на рабочей машине все работает отлично.

Мы планируем улучшить качество обфускации в версии 3.1

Еще, забыл момент.
Демоприложение собиралось со всеми отключенными опциями (защита памяти etc) проверялась только работа самого механизма защиты.
Под виртуалкой (ХР2 32 в среде от Парралелей) приложение закрывается сразу после появления сообщения о том, что защищено демонстрационной версией. (в отладчике умираем со статусом DEADCODE).
При этом на рабочей машине все работает отлично.

Скорее всего включена опция “Обнаружение средств виртуализации” и закрытие приложения происходит по факту обнаружения Parallels.

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

sysenter/int 2E/ PUSH ret_addr + push call_addr+ ret/ call FS:[0xC0] {WOW64}, ну или обычный PUSH ret_addr + JMP call_addr

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

этот вопрос достаточно серьезный, т.к. старый протектор, от которого отказываемся (криптор) такие инструкции в принципе эмулировал и не хочется перелопачивать целые куски ядра защиты, в связи с невозможностью их частичной эмуляции в VM.

Можете прислать простейший пример (оригинал EXE+MAP+VMP файлы), который не работает после VMProtect?

https://dl.dropboxusercontent.com/u/70911765/vmprotect_test.zip

исходник, бинарник, map и VMP

Единственный нюанс - не заработает если стоят антивирусы сплайсящие точки входа у используемых АПИ (DrWeb к примеру) и на Windows 10 64 бита.
В этом случае я не смогу получить чистые SDT индексы, поэтому лучше в песочнице запускать с чистой OS.

Примерчик старый, но суть в нем отображена полностью.

Проблема заключается в следующем:

004198B9 	lea eax, [004198DB]
004198BF 	push eax
004198C0 	push eax
004198C1 	movzx eax, byte ptr [ebp-11]
004198C5 	or eax, eax
004198C7 	jnz 004198D0 ↓
004198C9 	mov edx, esp
004198CB 	mov eax, [ebp-10]
004198CE 	sysenter
004198D0 	pop eax
004198D1 	lea edx, [esp+04]
004198D5 	mov eax, [ebp-10]
004198D8 	int 2E
004198DA 	nop
004198DB 	mov [ebp-18], eax

При виртуализации этого куска протектор ничего не знает про неявный переход на 004198DB (lea eax, [004198DB] + push). В результате прыжка по этому адресу происходит падение, т.к. на этом месте надодится уже совершенно другой код (или пикод от ВМ или кусок исполнителя ВМ). Чтобы пример заработал нужно поменить 004198DB (и все аналогичный конструкции ниже) как “внешний адрес” через контекстное меню в дизасме.

спасибо, понял, проверю

Тогда еще вопрос, получается что мне при каждой сборке придется ручками помечать все такие внешние адреса, дабы не происходило падение.
А есть ли вариант это как-то оптимизировать на основе меток (код я вам прислал). В частности возьмем рассмотренный Вами выше код:

      lea  eax, @FINALIZE
      push eax
      push eax
      movzx eax, NeedInt2E
      or   eax, eax
      jnz  @NT_CODE
      mov edx, esp
      mov eax, SysCallArgument
      sysenter

@FINALIZE:

можно ли как-то пометить маркер “@FINALIZE” чтобы о нем знал VMProt - к примеру той-же инлайн меткой как это применялось в крипторе.

В таком случае при каждом покрытии протектор сам будет держать в уме адреса таких внешних переходов и избавит разработчика от рутинной конфигурации релизного продукта.
Я уж не говорю о том, что на билдсервере автоматом выставить такие маркеры просто не представляется возможным, а вы сами знаете сколько сборок за день может быть сделано.

можно ли как-то пометить маркер “@FINALIZE” чтобы о нем знал VMProt - к примеру той-же инлайн меткой как это применялось в крипторе.

А как это сделано в крипторе?

В любом случае я бы предложил вынести вызов sysenter/int2e в отдельную функцию:

@SYSENTER:
mov     edx,esp
sysenter
ret

а затем вызывать его уже по месту:

      movzx eax, NeedInt2E
      or   eax, eax
      jnz  @NT_CODE
      mov eax, SysCallArgument
      call SYSENTER
      jmp @FINALIZE
@NT_CODE:
      ...
@FINALIZE:

При таком подходе вообще ничего не нужно будет делать на стороне VMProtect (кроме как добавить SYSENTER и INT2E в список защищаемых функций).

У него помечались куски кода которые нужно было виртуализировать.

выглядело как

{$I crypt_xxx.inc}

внутри было 4 ничего не делающих джампа:

asm db   $EB, $06, $EB, $FC, $EB, $FC, $FF, $F8 end;

при покрытии он искал эти метки и виртуализировал код между ними.
У вас за это отвечают ваши внутренние апи.

Вот предлагаю сделать еще одну, которую разработчик может поставить в начало такого внешнего перехода и vmprot будет на них ориентироватся.

Так это же ничего не даст, ошибка, как вы сами сказали, в адресе обработчика финализации, а не в вызове sysenter.
Да и еще, этот адрес размещенный на стеке, на него прыгает код из ядра после выполнения (т.е. он нужен для самого sysenter) т.о. вынос в отдельную процедуру не даст результата

Так это же ничего не даст, ошибка, как вы сами сказали, в адресе обработчика финализации, а не в вызове sysenter.

Немного не так. VMprotect ничего не знает про LEA+PUSH+SYSENTER, а а вот про CALL он как раз знает, что на стек нужно поместить адрес возврата независимо от того что идет после CALL (в случае виртуализации CALL на стек запишется адрес переходника, который убежит обратно в ВМ выполнять следующую за CALL инструкцию).

Да, но адрес возврата для sysenter это как раз вот этот пресловутый lea+push :slight_smile:

Да, но адрес возврата для sysenter это как раз вот этот пресловутый lea+push > :slight_smile:

А зачем вам тогда вот эта штука? :slight_smile:)

mov edx, esp

На самом деле EDX - это указатель на стек, откуда будет взят адрес возврата, поэтому совершенно без разницы откуда он там появится - как результат LEA+PUSH или как результат от CALL.

P.S. Походу у вас там еще и проблемы со стеком (один из push eax лишний)

На самом в вашем коде проблемы еще и в ветке IsWow64Process == 1 (все тотже самый неявный переход на адрес, о котором VMProtect ничего не знает):

0041989D 		lea eax, [004198B4]
004198A3 		push eax
004198A4 		push eax
004198A5 		mov eax, [ebp-0C]
004198A8 		push eax
004198A9 		mov eax, [ebp-10]
004198AC 		xor ecx, ecx
004198AE 		lea edx, [esp+0C]
004198B3 		ret

До работы доеду, распишу более подробно.
А этот код отрабатывает на 64 битных системах, переходник на wow64 шлюз :slight_smile:

Да, я уже дошел до JMPFAR, поэтому эту штуку лучше вынести в отдельную функцию и вызывать её через CALL по аналогии с SYSENTER/INT2E