Themida - обновлённый XProtector

Дата публикации 15 ноя 2005

Themida - обновлённый XProtector — Архив WASM.RU

        Прошло уже больше года с момента написания статьи про XProtector версии 1.07, с тех пор многое поменялось и многое из того, что было описано уже устарело. Вот собственно поэтому и выходит новая статья по этому протектору. Стоит сказать сразу, что стать скорее описание того, как исследовать протектор а не руководство по распаковке.

        При исследовании использовалась ОС Windows XP SP2, для изучения желательно иметь установленную WinXP или Win2003, дизассемблер IDA(f.e. 4.80), Import Reconstructor, HEX-редактор, PE Tools with Extreme Dumper Plugin(причём не для дампа самого процесса) а также некоторые специфические инструменты, такие как эмулирующий отладчик, плагин, удаляющий мусорный код, и простенькая утилита R0cmd, которая использовалась ещё год назад и была в исходниках к предыдущей статье. Подопытная программа - WinLicense Demo at 01.11.2005

        Общие принципы работы защиты остались те же, используется драйвер, который открывает доступ к IDT любому процессу, передавшему запрос драйверу oreans.sys, причём неважно будет ли этот процесс защищённой программой или эксплоитом для получения прав администратора, проникающем в ring0 с помощью этого вспомогательного драйвера протектора. Именно свободное использование IDT не даёт свободно отлаживать защищённую программу, т.к. протектор использует отладочные прерывания под свои нужды, например размещает свой обработчик в int1 или int3 и расшифровывает лежащий впереди код находясь в нулевом кольце. Абсолютно также как и раньше используется перехват функций ядра с помощью SST, по тем же принципам формируются переходники на импортируемые функции и защищается пользовательский код макросами SDK. Раз так, значит снимать защиту будет точно также - с помощью подгружаемой в адресное пространство своей DLL. В сопровождающем архиве приведен исходник этой DLL, принцип её действия это перехват всевозможных полезных событий и ведение лога с указанием адресов и прочих параметров.

  Антидамповая защита и антиотладка:

        Выще уже упоминалось, что перехват функций для работы с памятью происходит аналогично XProtector'у, но вот есть одна проблема, при подгрузке оригинальной sst проискохит перезагрузка, именно это и надо устранить, чем сейчас и займёмся.

        Логика такова, если происходит перезагрузка, причём сразу же после восстановления sst, то очевидно что протектор проверяет, на месте ли его обработчики функций. В таком случае проверка может быть в двух местах - в одном из многочисленных потоков, порождённых протектором, или во время переключения потоков либо процессов. Проверяется это легко, достаточно остановить все потоки, заморозив тем самым защищённый процесс. Делается это примерно так, внедрённая DLL собирает идентификаторы всех создаваемых потоков в таблицу, в любой момент можно вызвать SuspendThread в цикле, остановив их все, только последним надо останавливать текущий поток. После заморозки процесса восстановить SST не удаётся, следовательно этот вариант неверный и код проверки SST и IDT находится в ядре и вызывается при переключении контекстов. Для поиска применим такой оригинальный способ: делаем дампы главных модулей ядра Windows - ntoskrnl.exe(или аналога) и hal.dll дважды - до и после запуска протектора(вот именно для этого и нужен плагин extreme dumper). Если есть перехваты, они обнаружаться простым сравнением сравнением секций кода в дампах. Итак, сравнение дампов ntoskrnl:

 B78C и др. - внутренности sst shadow, не интересно.
 14938 - преобразовав это файловое смещение в RVA(для удобства можно использовать FLC из PE Tools, а ntoskrnl.exe загрузить в IDA по той базе, по которой она загружена у вас в памяти), выходим на функцию KeAttachProcess, которая используется для переключения в адресное пространство другого процесса и считается устаревшей, также не интересно.
 31E4D - vsprintf, можно даже не разбираться сразу видно что это не то, что нужно.

  смотрим hal.dll:

 2278 - KfRaiseIrql, вот это уже очень интересно, т.к. эта функция используется ядром очень активно, в том числе и при переключении процессов. Смотрим, что же делает протектор при перехвате(адрес опять же проще поправить ImageBase в заголовке тому дампу где есть перехват и просто посмотреть адрес в IDA):

Код (Text):
  1.  
  2.  
  3. seg002:F8AAB000                 pusha
  4. seg002:F8AAB001                 call    $+5
  5. seg002:F8AAB006                 pop     ebp
  6. seg002:F8AAB007                 sub     ebp, 4CAFD95h
  7.  
  8.  ;Обновление переменной, содержащей число тиков таймера(не интересно для нас)
  9. seg002:F8AAB00D                 mov     eax, 80551280h  ; KeTickCount
  10. seg002:F8AAB012                 mov     eax, [eax]
  11. seg002:F8AAB014                 cmp     dword ptr [ebp+xvTickCount], 0
  12. seg002:F8AAB01B                 jnz     short loc_F8AAB023
  13. seg002:F8AAB01D                 mov     [ebp+xvTickCount], eax
  14. seg002:F8AAB023
  15. seg002:F8AAB023 loc_F8AAB023:                           ; CODE XREF: seg002:F8AAB01B
  16. seg002:F8AAB023                 mov     ebx, [ebp+xvTickCount]
  17. seg002:F8AAB029                 add     ebx, 78h ; 'x'
  18. seg002:F8AAB02C                 cmp     eax, ebx
  19. seg002:F8AAB02E                 jbe     _return_1
  20. seg002:F8AAB034                 mov     [ebp+xvTickCount], eax
  21.  
  22.  ;DR7 - управляющий отладочный регистр, десятый бит обязан быть установлен;
  23.  ;остальный биты сбрасываются - такие образом удаляются все 4 брэйкпоинта(bpm)
  24. seg002:F8AAB03A                 mov     edx, 801BFAh
  25. seg002:F8AAB03F                 sub     edx, 8017FAh
  26. seg002:F8AAB045                 mov     dr7, edx        ; edx = 00000400
  27.  
  28.  
  29.  ;загрузка в eax адреса IDT..
  30. seg002:F8AAB048                 mov     eax, 0AB57712h
  31. seg002:F8AAB04D                 sub     eax, 8AB18312h
  32. seg002:F8AAB052                 add     eax, 0Ch        ; int1 descriptor
  33. seg002:F8AAB055                 or      byte ptr [eax+1], 1100000b ; set DPL to 3
  34.  
  35.  ;читаем адрес обработчика
  36. seg002:F8AAB059                 mov     ecx, cs:[eax]
  37. seg002:F8AAB05C                 mov     cx, cs:[eax-4]  ; ecx = address of handler
  38.  
  39.  ;переход далее, если обработчик находится не в ядре
  40. seg002:F8AAB061                 cmp     ecx, 0FFFF0000h
  41. seg002:F8AAB067                 jnb     short _int3_check
  42. seg002:F8AAB069                 cmp     ecx, 80000000h
  43. seg002:F8AAB06F                 jb      short _int3_check
  44.  
  45.  
  46.  ;а вот это интересно, если адрес обработчика находится в ядре, то он меняется на 0xFFFFFFFF,
  47.  ;это значит что пока функция KfRaiseIrql перехвачена, восстановить IDT невозможно
  48. seg002:F8AAB071                 push    ds
  49. seg002:F8AAB072                 mov     ecx, 10h
  50. seg002:F8AAB077                 db      66h
  51. seg002:F8AAB077                 mov     ds, cx
  52. seg002:F8AAB07A                 assume ds:nothing
  53. seg002:F8AAB07A                 mov     cx, 0FFFFh
  54. seg002:F8AAB07E                 mov     [eax-4], cx
  55. seg002:F8AAB082                 mov     [eax+2], cx     ; set handler address to 0xFFFFFFFF
  56. seg002:F8AAB086                 pop     ds
  57. seg002:F8AAB087                 assume ds:seg003
  58.  
  59.  ;далее аналогично обрабатывается ещё одно отладочное прерывание
  60.   ...
  61.  
  62. seg002:F8AAB0DB _pf_test:                               ; CODE XREF: seg002:F8AAB0D4
  63. seg002:F8AAB0DB                 mov     esi, 0ED1DBFE1h
  64. seg002:F8AAB0E0                 add     esi, 60F5D3h
  65. seg002:F8AAB0E6                 cmp     byte ptr [esi], 0
  66. seg002:F8AAB0E9                 jz      loc_F8AAB1B4
  67.  
  68.  ;здесь читается адрес обработчика исключения #PF - page fault
  69.  ;если IDT была восстановлена, то перезагружаем компьютер :)
  70. seg002:F8AAB0EF                 mov     esi, 0AB57712h
  71. seg002:F8AAB0F4                 sub     esi, 8AB18312h
  72. seg002:F8AAB0FA                 add     esi, 74h ; 't'
  73. seg002:F8AAB0FD                 mov     eax, [esi]
  74. seg002:F8AAB0FF                 mov     ax, [esi-4]
  75. seg002:F8AAB103                 mov     esi, 9D0B83C2h
  76. seg002:F8AAB108                 xor     esi, 65A4B3C2h
  77. seg002:F8AAB10E                 cmp     esi, eax
  78. seg002:F8AAB110                 jz      short loc_F8AAB117
  79. seg002:F8AAB112                 jmp     _reboot
  80.  
  81. seg002:F8AAB117 loc_F8AAB117:                           ; CODE XREF: seg002:F8AAB110
  82. seg002:F8AAB117                 mov     ecx, 202h
  83. seg002:F8AAB11C                 call    _hash1_calc
  84. seg002:F8AAB121                 mov     ebx, eax
  85. seg002:F8AAB123                 add     esi, 206h
  86. seg002:F8AAB129                 mov     ecx, 32h ; '2'
  87. seg002:F8AAB12E                 call    _hash1_calc
  88. seg002:F8AAB133                 add     eax, ebx
  89. seg002:F8AAB135                 mov     ebx, 0ECF96E68h
  90. seg002:F8AAB13A                 add     ebx, 854748h
  91.  
  92.  ;это то, чего не было в xprotector'е и есть в themid'е - проверка контрольной суммы обработчика
  93.  ;если пропатчен обработчик - перезагрузка
  94. seg002:F8AAB140                 cmp     [ebx], eax
  95. seg002:F8AAB142                 jz      short _pf_nopatch
  96. seg002:F8AAB144                 jmp     _reboot
  97.  

        Далее идёт однотипный код, проверяющий исключение #NP(неприсутствующего сегмента) и что более важно некоторых функций sst. Именно поэтому восстановление sst при "живом" перехвате KfRaiseIrql невозможно. Способ обхода очевиден - восстанавливаем 5 оригинальных байт обработчика этой функции hal.dll и вперёд, можно восстанавливать sst. Правда есть одно но, в разных версиях протектора перехватываются разные функции в hal.dll, поэтому предлагаю универсальный способ - после загрузки ОС снимать дамп секции кода hal.dll(можно за одно и ntoskrnl) и когда необходимо снять дамп, восстановить импорт или вообще просто открыть доступ к памяти, просто записываем на место оригинальную секцию кода и подгружаем sst с диска, всё, антидампа как не бывало. Можно даже восстановить IDT и отлаживать защищённый процесс ring3 отладчиками, правда только до первой ring0-decrypt конструкции, толку от такой отладки практически не будет.

  Нахождение и восстановление кода на OEP:

        Themida имеет опцию защиты, позволюющую прятать оригинальную точку входа программы, чего не было в xprotector'е. Делается это общеизвестным способом, инструкции крадутся с OEP, разбавляются мусором и отправляются куда-нибудь подальше в память. Чтобы научится ловить эти инструкции и вообще переход на OEP, надо знать, где конкретно располагаются эти краденные инструкции. Чтобы это узнать используем "троянский принцип" - подкинем протектору инструкцию, вызывающую исключение. А конкретно берём любую программу(например всеми любимый calc.exe), меняем первый байт на OEP на 0xFA(инструкция cli, причём лучше два байта 0xFA подряд на OEP вписать, чтобы структуру кода не нарушить), запаковываем его WinLicense, не забыв указать опцию прятания OEP и дело сделано - при начале исполнения программы будем получать исключение EXCEPTION_PRIV_INSTRUCTION. Т.к. мы имеем в адресном пространстве свою DLL, то ничто не мешает перехватить это исключение, и VEH подходит для этого как нельзя лучше, т.к. векторный обработчик исполняется первым и для всех потоков процесса. Добавляем код, который выдаёт MessageBox при исключении привилегированной инструкции с заголовком, показывающим адрес исключения выдаст, снимаем дамп и можно изучать код на OEP. Код, приведённый ниже обработан обновленным unscrambled-плагином:

Код (Text):
  1.  
  2.  
  3.  ;Это две инструкции cli, которые мы записали на EP calc.exe
  4. WinLicen:0123E87D                 cli
  5. WinLicen:0123E895                 cli
  6.  
  7.  ;А вот эти две инструкции заменяют одну - push 010015E0h
  8. WinLicen:0123E907                 push    0C6301166h
  9. WinLicen:0123E924                 add     dword ptr [esp], 3AD0047Ah
  10.  
  11.  ;Здесь краденный с OEP код закончился
  12.  ;1247Ch - это RVA первой некраденной инструкции на OEP
  13. WinLicen:0123E92E                 push    1247Ch
  14. WinLicen:0123E933                 pushf
  15. WinLicen:0123E934                 cld
  16. WinLicen:0123E9CE                 push    eax
  17. WinLicen:0123EA16                 push    eax
  18. WinLicen:0123EA40                 mov     [esp], ebp
  19. WinLicen:0123EA61                 clc
  20. WinLicen:0123EA98                 call    $+5
  21. WinLicen:0123EA9D                 pop     ebp
  22. WinLicen:0123EA9E                 sub     ebp, 0AA7D278h
  23.  
  24.  ;Синхронизация(возможно сигнал окончиния распаковки)
  25. WinLicen:0123EB01                 mov     eax, [ebp+0A940E25h]
  26. WinLicen:0123EB07                 cld
  27. WinLicen:0123EB08                 mov     byte ptr [eax], 0
  28. WinLicen:0123EB2F                 mov     eax, [ebp+0A942385h]
  29. WinLicen:0123EB64                 pusha
  30. WinLicen:0123EB78                 pushf
  31.  
  32.  ;Здесь мы видим код, затирающий краденные байты, чтобы их нельзя было восстановить
  33.  ;простым дампом во время выполнения, из этого следует, что придётся ловить окончание
  34.  ;распаковки до выполнения этого блока кода
  35. WinLicen:0123EBCD                 call    $+5
  36. WinLicen:0123EBD2                 pop     edi
  37. WinLicen:0123EC42                 mov     eax, 0
  38. WinLicen:0123EC72                 mov     edx, edi
  39. WinLicen:0123EC93                 lea     ecx, [ebp+0AA7D108h]
  40. WinLicen:0123ECED                 sub     edx, ecx
  41. WinLicen:0123ED05                 sub     edi, edx
  42. WinLicen:0123ED24                 mov     ecx, 0F8h
  43. WinLicen:0123ED29                 cld
  44. WinLicen:0123ED2B                 sub     edi, ecx
  45. WinLicen:0123ED68                 lea     esi, [ebp+0AA7D108h]
  46. WinLicen:0123ED86                 lea     edx, [ebp+0AA7D5E9h]
  47. WinLicen:0123EDE6                 sub     edx, esi
  48. WinLicen:0123EDEA                 add     ecx, edx
  49. WinLicen:0123EE0E                 rep stosb
  50. WinLicen:0123EE10                 inc     edx
  51. WinLicen:0123EE11                 stosd
  52.  
  53.  ;выравнивание стека и переход в секцию кода программы
  54. WinLicen:0123EE8D                 popf
  55. WinLicen:0123EEAA                 cmc
  56. WinLicen:0123EEAB                 popa
  57. WinLicen:0123EED1                 pop     ebp
  58. WinLicen:0123EF12                 add     [esp+8], eax
  59. WinLicen:0123EF16                 cld
  60. WinLicen:0123EF17                 pop     eax
  61. WinLicen:0123EF91                 popf
  62. WinLicen:0123EF92                 retn
  63.  

        Как видно с помощью плагина не составляет труда восстановить украденные инструкции и найти истинную OEP, но этого мало, надо ещё научится останавливать поток перед тем, как код будет затёрт нулями, но после того, как он будет расшифрован. Немного поисследовав код в окрестностях можно сделать несколько выводов. Во-первых краденые байты лежат практически в самом конце секции протектора. Во-вторых до передачи управления на эти байты они зашифрованы, и расшифровываются одним из ring0-дешифровщиков, лежашим чуть выше. А для вызова дешифровщика необходимо записать в IDT его адрес. Смотрим, как протектор это делает:

Код (Text):
  1.  
  2.  
  3.  ;загружаем в eax адрес выхода из прерывания(после окончания работы дешифровщика инструкция
  4.  ;iret передаст управление на этот адрес), а в esi - адрес самого обработчика
  5. WinLicen:0123D181                 lea     eax, [ebp+0AA7D009h]
  6. WinLicen:0123D199                 lea     esi, [ebp+0AA7C1D0h]
  7.  
  8.  ;Если вдруг страница с обработчиком прерывания будет сброшена в файл подкачки, то
  9.  ;вместо дешифровки кода появится синий экран. Эти команды подгружают страницы в таком случае
  10. WinLicen:0123D1BC                 mov     edx, [eax]
  11. WinLicen:0123D1E6                 mov     [eax], edx
  12. WinLicen:0123D221                 mov     edx, [eax+12Ch]
  13. WinLicen:0123D262                 mov     [eax+12Ch], edx
  14.  
  15.  ;...
  16.  
  17.  ;А здесь мы видим вызов функции, устанавливающей обработчики прерываний int1 и int3.
  18.  ;Адрес передаётся через стек. Обратите внимание, что функция перезаписывает IDT для
  19.  ;всех процессоров в системе
  20. WinLicen:0123D703                 xchg    eax, esi
  21. WinLicen:0123D72C                 push    eax
  22. WinLicen:0123D73B                 xchg    eax, esi
  23. WinLicen:0123D746                 lea     edx, [ebp+xf_set_dbg_int]
  24. WinLicen:0123D765                 call    edx
  25.  
  26.  ;...
  27.  
  28.  ;Начало обработчика 3-его прерывания
  29. WinLicen:0123D9F5                 mov     [esp], eax
  30. WinLicen:0123DA1C                 push    0
  31. WinLicen:0123DA1F                 lea     eax, [ebp+0AA7D009h]
  32. WinLicen:0123DA3D                 add     eax, 5
  33. WinLicen:0123DA82                 push    eax
  34. WinLicen:0123DA9C                 push    eax
  35. WinLicen:0123DAA8                 mov     [esp], eax
  36.  
  37.  ;В каждом подобном обработчике сбрасываются отладочные регистры
  38. WinLicen:0123DAC9                 sub     eax, eax
  39. WinLicen:0123DAF7                 mov     dr0, eax
  40. WinLicen:0123DB36                 mov     dr1, eax
  41. WinLicen:0123DB74                 mov     dr2, eax
  42. WinLicen:0123DBC3                 mov     dr3, eax
  43. WinLicen:0123DBF0                 pop     eax
  44. WinLicen:0123DBF1                 cld
  45. WinLicen:0123DBF2                 mov     edx, [esp+4]
  46. WinLicen:0123DC2C                 mov     edi, [esp]
  47. WinLicen:0123DC6E                 mov     ecx, 0
  48. WinLicen:0123DC99 ; ---------------------------------------------------------------------------
  49. WinLicen:0123DC99
  50. WinLicen:0123DC99 loc_123DC99:                            ; CODE XREF: WinLicen:0123E512
  51. WinLicen:0123DC99                 mov     eax, 400h
  52. WinLicen:0123DC9E                 mov     dr7, eax
  53.  
  54.  ;...
  55.  
  56.  ;Код впереди на данный момент расшифрован, записываем 0xFFFFFFFF на место(т.е. в качестве
  57.  ;обработчиков int1 и int3). Если вспомнить код обработчика перехваченной функции KfRaiseIrql,
  58.  ;то он записывал 0xFFFFFFFF в IDT только если обработчики 1-ого и 3-его прерывания находились
  59.  ;в ядре, а сейчас адрес обработчиков - 0123D9F5h, поэтому протектор и вызывает снова функцию
  60.  ;xf_set_dbg_int, чтобы деактивировать отладочные прерывания
  61. WinLicen:0123E5B7                 push    8BF57B2Ah
  62. WinLicen:0123E5C3                 xor     dword ptr [esp], 740A84D5h
  63. WinLicen:0123E5EF                 lea     ecx, [ebp+xf_set_dbg_int]
  64. WinLicen:0123E61D                 call    ecx
  65. WinLicen:0123E64B                 cld
  66.  
  67.  ;Это видимо для подстраховки, дублирование xf_set_dbg_int только для текущего процессора
  68.  ;(т.е. для того, на котором выполняется этот код)
  69. WinLicen:0123E64C                 push    eax
  70. WinLicen:0123E667                 mov     [esp], eax
  71. WinLicen:0123E69F                 sidt    qword ptr [esp-2]
  72. WinLicen:0123E6B7                 pop     eax
  73. WinLicen:0123E6B9                 add     eax, 0Ch
  74. WinLicen:0123E6D3                 mov     word ptr [eax-4], 0FFFFh
  75. WinLicen:0123E6F4                 mov     word ptr [eax+2], 0FFFFh
  76. WinLicen:0123E718                 push    eax
  77. WinLicen:0123E738                 mov     [esp], eax
  78. WinLicen:0123E759                 sidt    qword ptr [esp-2]
  79. WinLicen:0123E779                 pop     eax
  80. WinLicen:0123E784                 add     eax, 1Ch
  81. WinLicen:0123E79E                 mov     word ptr [eax-4], 0FFFFh
  82. WinLicen:0123E7A5                 mov     word ptr [eax+2], 0FFFFh
  83. WinLicen:0123E7AC                 mov     ecx, [ebp+0A9420A1h]
  84. WinLicen:0123E7CA                 mov     byte ptr [ecx], 0
  85. WinLicen:0123E7CD                 stc
  86. WinLicen:0123E7CE
  87. WinLicen:0123E7CE loc_123E7CE:                            ; CODE XREF: WinLicen:0123E570
  88. WinLicen:0123E7CE                 pop     ecx
  89. WinLicen:0123E7F1                 add     esp, 8
  90.  
  91.  ;Выход обратно в ring3
  92. WinLicen:0123E80E                 iret
  93.  

        Наиболее простой путь это перехват внутренней функции протектора - xf_set_dbg_int. Пусть наш код будет скидывать в лог адреса вызова(точнее адреса следующие за вызовом) перехваченной функции, когда этим адресом окажется 0123E61Fh, можно спокойно снимать дамп, определять положение OEP и восстанавливать краденные байты. Правда остаются проблемы - как найти функцию xf_set_dbg_int внутри защищённой программы и в какой момент её перехватывать. Искать можно несколькими способами, например найти любой ring0-decryptor в дампе и вызов этой функции, но есть способ проще и лучше - по сигнатуре. Внутренности этой функции не менялись со времён xprotector'а, поэтому поиск по сигнатуре будет работать абсолютно со всеми версиями протектора. А перехватывать лучще всего в момент, когда DLL получит уведомление о запуске последнего треда перед окончанием распаковки. Номер его можно посмотреть в логе, например в упакованном calc.exe последний тред имеет номер 28(включая главный). Также надо ещё помнить, что наш обработчик xf_set_dbg_int после расшифровки спертых с OEP байт будет выполняться в ring0 и перед тем, как снимать дамп необходимо вернуться в ring3(например чтобы вывести MessageBox с сообщением о том, что распаковка окончена). Для этого подменяем адрес возврата для инструкции iret прямо в стеке. Рассчитываем смещение в стеке: допустим при выполнение инструкции iret по адресу 0123E80Eh адрес возврата лежит по смещению 0. Поднимаемся вверх до call'а на 0123E61D и получаем что адрес возврата здесь: [esp + 0Ch]. Но учитывая, что мы находимся внутри процедуры, надо добавить ещё - 8(т.к. при вызове xf_set_dbg_int в стек кидается адрес возврата из функции и 1 параметр), получаем [esp + 14h]. Но в самом начале обработчика находится инструкция pushad, уменьщающая стек на 20h, в итоге адрес возврата будет находится по адресу [esp + 34h] внутри нашего обработчика xf_set_dbg_int. Сохраняем куда-нибудь старый адрес, и записываем на это место какой-нибудь свой адрес, где будет находится вызов MessageBox, выводящий сообщение "Stolen bytes at 0XXXXXXXXh". Теперь рассмотрим всё это подробнее на примере нашей цели - WinLicense Demo.

Код (Text):
  1.  
  2.  
  3.  ;Это небольшой кусок кода из внедрямой DLL, а конкретно всё, что касается
  4.  ;поиска OEP и краденный байт
  5.  
  6. cmp reason, DLL_THREAD_ATTACH
  7. jnz @@reason2
  8.  
  9.  ;========== Thread create processing ==========;
  10.     assume fs:NOTHING
  11.  
  12.  ;Читаем и сохраняем идентификатор нового потока, а также записываем сообщение в лог
  13. mov eax, DWORD PTR fs:[24h]
  14. mov edx, ThreadCounter
  15. mov [offset threads + edx*4], eax
  16.  
  17.  @@l1:
  18. inc ThreadCounter
  19. invoke  wsprintf, offset buffer1, offset fmt003, ThreadCounter, DWORD PTR fs:[24h]
  20. invoke  LogWrite, offset buffer1
  21.     assume fs:ERROR
  22.  
  23.  ;Здесь надо указать номер последнего треда, который создаётся <I>протектором,</I>
  24.  ;в WinLicense demo этот номер равен 26, все последующие создаёт уже пользовательский код
  25. cmp ThreadCounter, 26
  26. jnz @@exit
  27.  
  28.  
  29.  ;Поиск xf_set_dbg_int по сигнатуре
  30. invoke  find_signature, MHandle, ImageSize, offset xf_set_dbg_int_sign
  31. test    eax, eax
  32. jz  @@l2    ;если не найдено
  33.  
  34.  ;Вывод в лог адреса найденной функции
  35. mov ebx, eax
  36. invoke  wsprintf, offset buffer1, offset fmt005, ebx
  37. invoke  LogWrite, offset buffer1
  38.  
  39.  ;Перехват функции
  40. mov eax, offset h_xf_set_dbg_int_sign
  41. mov BYTE PTR [ebx], 0E9h
  42. sub eax, ebx
  43. sub eax, 5
  44. mov DWORD PTR [ebx+1], eax
  45.  
  46.  ;Сохраняем адрес для перехода на него из нашего обработчика
  47. add ebx, 5
  48. mov _xf_set_dbg_int, ebx
  49. jmp @@exit
  50.  
  51.  ;Вывод в лог сообшения о том, что функция xf_set_dbg_int не обнаружена
  52.  @@l2:
  53. invoke  LogWrite, offset mess002
  54. jmp @@exit
  55.  

        Количество тредов можно ввести максимальное и уменьшать, пока количество изменений не возрастёт резко до несколько десятков тысяч. При запуске создаётся 29 тредов, но 3 последние - порождение уже не протектора а пользовательского кода. Теперь рассмотрим наш обработчик xf_set_dbg_int_sign:

Код (Text):
  1.  
  2.  ;Вот этот адрес надо будет определить, а точнее переписать из лога
  3. LAST_IDTACCESS      equ     0BE8668h
  4.  
  5.  ;Об этих константах рассказано далее
  6. ;_offset1       equ     0
  7. ;_sleeparg      equ     0
  8.  
  9. h_xf_set_dbg_int_sign proc
  10. pusha
  11.  
  12.  ;Увеличение счётчика обращений к IDT
  13. inc idtacc_c
  14.  
  15.  ;"Обновление" переменной - максимально большего адреса обращения к IDT
  16. mov eax, [esp + 20h]
  17. cmp eax, top_idtacc
  18. jbe @@exit
  19.  
  20. mov top_idtacc, eax
  21.  
  22.  ;Распаковка окончена?
  23. cmp eax, LAST_IDTACCESS
  24. jnz @@exit
  25.  
  26.  ;_offset1 - смещение адреса возврата с ring0-декриптора,
  27.  ;о том как его искать было рассказано выше
  28.  
  29.   ifdef _offset1
  30. mov eax, [esp + _offset1]
  31. mov oep, eax
  32. mov DWORD PTR [esp + _offset1], offset @@oep
  33.   else
  34. invoke  MessageBox, NULL, offset mess003, offset mess, MB_OK
  35.   endif
  36.  
  37. jmp @@exit
  38.  
  39.  @@oep:
  40. pusha
  41.  
  42.  ;...
  43.  
  44.   ifdef _sleeparg
  45. mov BYTE PTR ds:[_sleeparg], 0FFh
  46.   endif
  47.  
  48.  
  49.  ;...
  50.  
  51. invoke  MessageBox, NULL, offset mess004, offset mess, MB_OK
  52. popa
  53. cli ;исключение обеспечит корректный выход на 100%
  54.  
  55.  
  56.  @@exit:
  57. mov     ebx, [esp+24h]  ;инструкция с начала перехваченной функции в протекторе
  58. jmp _xf_set_dbg_int
  59. h_xf_set_dbg_int_sign endp
  60.  

        После определения количество тредов и адреса последнего обращения к IDT можно снимать дамп и изучать окрестности OEP. Адреса этих самых окрестностей нужно брать из лога: top address 0XXXXXXXXh - адрес, где меняется IDT, будет известен при перехвате xf_set_dbg_int, oep(?) = 0XXXXXXXXh - адрес выхода из последнего перед OEP дешифровщика нулевого кольца, этот будет известен, только после определения смещения в стеке этого самого адреса

Код (Text):
  1.  
  2.  ;Вызов xf_set_dbg_int, однако "окружение" этого вызова выглядит совсем не так, как в
  3.  ;запакованном calc.exe, потому что это другой тип ring0-decryptor'а
  4.  ;Высчитывая смещение стека, где хранится EIP для инструкции iret:
  5.  ;-20(смещение стека перед вызовом) -8(адрес возврата и параметр) - 20(инструкция pusha
  6.  ;в начале h_xf_set_dbg_int_sign, приведённой выше) получаем -48, вписываем
  7.  ;константу _offset1 равную 48h в исходник DLL
  8. WinLicen:00BE865D                 push    0FFFFFFFFh
  9. WinLicen:00BE8662                 call    dword ptr [ebp+4BF2A11h]
  10. WinLicen:00BE8668                 push    ecx   ;-20
  11. WinLicen:00BE8669                 sidt    qword ptr [esp-2]
  12. WinLicen:00BE866E                 pop     ecx   ;-24
  13. WinLicen:00BE866F                 add     ecx, 0Ch
  14. WinLicen:00BE8672                 mov     word ptr [ecx-4], 0FFFFh
  15. WinLicen:00BE8678                 mov     word ptr [ecx+2], 0FFFFh
  16. WinLicen:00BE867E                 mov     ecx, [ebp+4BF03E9h]
  17. WinLicen:00BE8684                 mov     byte ptr [ecx], 0
  18. WinLicen:00BE8687                 mov     eax, ebx
  19. WinLicen:00BE86AF                 popa  ;-20
  20. WinLicen:00BE86B0                 iret  ;0
  21.  
  22.  ;...
  23.  
  24. WinLicen:00BE86DA                 mov     dword ptr [ebp+4BF0449h], 4D20h
  25. WinLicen:00BE86ED                 mov     dword ptr [ebp+4BF12B9h], 0
  26. WinLicen:00BE870D                 push    dword ptr [ebp+4BF1DB5h]
  27. WinLicen:00BE8713                 mov     [ebp+4BF14C1h], esi
  28. WinLicen:00BE8719                 call    dword ptr [ebp+4BF1C61h]
  29. WinLicen:00BE87EC                 mov     esi, ecx
  30. WinLicen:00BE87EE
  31.  
  32.  ;Хмм.. бесконечный цикл. Очевидно, что по адресу 00BE8719h вызывается SetEvent
  33.  ;я правда не проверял, вдруг это не так :)
  34.  ;А по адресу 00BE87F0h - Sleep. Оба этих адреса лежат в heap'е, напомню, что некторые библиотеки,
  35.  ;включая kernel32.dll подгружаются с диска и функции вызываются минуя kernel32.dll в памяти
  36. WinLicen:00BE87EE loc_BE87EE:                             ; CODE XREF: WinLicen:00BE87F8
  37. WinLicen:00BE87EE                 push    0
  38. WinLicen:00BE87F0                 call    dword ptr [ebp+4BF2851h]
  39. WinLicen:00BE87F6                 mov     eax, eax
  40. WinLicen:00BE87F8                 jmp     short loc_BE87EE
  41.  

        Дальнейшую расщифровку будет выполнять другой тред, и нужно как-то поймать окончание этой расшифровки. Недолго думая вписываем вместо инструкции push 0 по адресу 00BE87EEh инструкцию push 0FFFFFFFFh(т.о. константа _sleeparg будет равна 00BE87EFh) тем самым делаю задержку более чем на 4 миллиарда секунд, которого уж точно хватит для того, чтобы тред успел закончить расшифровку и даже для того, чтобы снять дамп уже с готовыми к восстановлению краденными байтами:

Код (Text):
  1.  
  2.  ;замена push ebp
  3. WinLicen:00BE8859                 push    eax
  4. WinLicen:00BE886E                 mov     [esp], ebp
  5.  
  6. WinLicen:00BE8891                 mov     ebp, esp
  7. WinLicen:00BE88BA                 add     esp, 0FFFFFFE0h
  8.  
  9.  ;push ebx
  10. WinLicen:00BE88F7                 xchg    eax, ebx
  11. WinLicen:00BE892D                 push    eax
  12. WinLicen:00BE897C                 xchg    eax, ebx
  13.  
  14. WinLicen:00BE897E                 sub     eax, eax
  15. WinLicen:00BE89C6                 mov     [ebp-20h], eax
  16. WinLicen:00BE89F3                 mov     [ebp-1Ch], eax
  17. WinLicen:00BE8A0C                 mov     [ebp-18h], eax
  18. WinLicen:00BE8A57                 mov     [ebp-14h], eax
  19. WinLicen:00BE8A9C                 mov     eax, offset dword_776200
  20.  
  21.  ;далее идёт код протектора, из которого нам полезна только следующая инструкция,
  22.  ;где в стек записывается RVA OEP + 0XXh, где 0XXh - размер украденных инструкций
  23. WinLicen:00BE8AA4                 push    376D22h
  24. WinLicen:00BE8AA9                 pushf
  25.  ;...
  26.  
Код (Text):
  1.  
  2.  
  3.  ;Вбиваем в FASM или HIEW эти инструкции, определяем их размер(26 байт),
  4.  ;из этого следует, что в WinLicense Demo OEP = 776D08h(RVA=376D08h)
  5.  ;и именно туда надо добавить эти инструкции
  6. push    ebp
  7. mov     ebp, esp
  8. add     esp, 0FFFFFFE0h
  9. push    ebx
  10. xor     eax, eax
  11. mov     [ebp-20h], eax
  12. mov     [ebp-1Ch], eax
  13. mov     [ebp-18h], eax
  14. mov     [ebp-14h], eax
  15. mov     eax, 776200h
  16.  

        На этом заканчивается работа над OEP. Теперь можно переходить к импорту.

  Восстановление импорта:

        В основном восстановление импорта мало чем отличается от xprotector'а, но некоторые моменты стоит упомянуть, особенно перехват API-функций. Ведь теперь протектор замечает изменение кода в native API функциях, но всё же есть пока способ не лезть в ядро. Каждое обращение к ядру идёт через функцию KiFastSystemCall, а адрес её записан здесь: 7FFE0300h, оттуда он каждый и читается. Запись в эту область памяти запрещена для кода режима пользователя, и к тому же эта область является общей для всех процессов, и если даже написать свой драйвер, который будет записывать туда что-нибудь по требованию система сразу рухнет, потому что все остальные процессы окажутся изолированными от ядра. Выход в том, что надо как-то сделать страницу памяти контекстно-зависимой. Как раз для этого Windows NT использует зарезервированный девятый бит в таблице страниц, установление его равнозначно вызову VirtualProtect с параметром PAGE_WRITECOPY. Т.е. чтобы перехватить обращения к ядру, надо установить в PTE страницы с адресом 7FFE0000h бит copy-on-write и можно спокойно перехватывать. Причём драйвер можно всё-таки не писать, ведь протектор открывает доступ к IDT, и никто не помешает нам использовать любое прерывание для того, чтобы попасть в ring0:

Код (Text):
  1.  
  2. hook_kifastsystemcall proc
  3.  
  4.  ;адрес дескриптора 255-ого прерывания
  5. mov edx, 8003F400h+0FFh*8
  6. mov eax, offset _intFF
  7. mov [edx], ax   ;младшие 16 бит смешения обработчика
  8. mov WORD PTR [edx+2], 08h   ;селектор обработчика
  9. shr eax, 16
  10. mov WORD PTR [edx+6], ax    ;старшие 16 бит
  11. or  WORD PTR [edx+4], 6000h ;DPL устанавливаем 3
  12.  
  13.  ;Вызываем 255-ое прерывание, в eax - адрес для установки флага copy-on-write
  14. mov eax, 7FFE0300h
  15. int 0FFh
  16.  
  17.  ;Собственно перехват
  18. mov DWORD PTR ds:[7FFE0300h], offset hKiFastSystemCall
  19. ret
  20. hook_kifastsystemcall endp
  21.  
  22.  ;...
  23.  
  24.  _intFF:
  25. invoke  set_copyonwrite_flag, eax   ;Вызов функции для установки флага
  26. iretd
  27.  
  28.  
  29.  ;Это новая функция KiFastSystemCall
  30.  hKiFastSystemCall proc
  31.  
  32.  ;Это необходимо для вызова функций API из перехваченной функции
  33.  ;Например записи в чего-нибудь в лог, чтобы не было ненужной рекурсии
  34. cmp hook_active, 1
  35. jz  @@systemcall
  36.  
  37.  
  38. pusha
  39. mov hook_active, 1
  40.  
  41.  ;Здесь можно размещать свой код для перехвата
  42.  
  43.  @@exit:
  44.  
  45.  ;Выход(почему-то masm не хочет ассемблировать инструкцию SYSENTER)
  46. popa
  47. mov hook_active, 0
  48. mov edx, esp
  49. db 0Fh, 034h    ;SYSENTER
  50.  
  51.  
  52.  @@systemcall:
  53. mov edx, esp
  54. db 0Fh, 034h    ;SYSENTER
  55. hKiFastSystemCall endp
  56.  

        А дальше полная аналогия с xprot-ом, перехватываем VirtualAlloc(только смещение адреса возврата из VirtualAlloc надо, это можно сделать в ollydbg или softice протрассировав вызов этой функции до инструкции SYSENTER и прибавив 20h уравновешивая инструкцию pusha в начале hKiFastSystemCall. В логе видно, что часто вызывается VirtualAlloc в адреса 0BD290Dh, это и есть процедура создания импорта, но если вписать этот адрес в исходник DLL, то можно устать закрывать MessageBox'ы, вписать туда надо адрес начала процедуры создания импорта, это 0BD209Ch. После сообщения "Imports creating begins; see log for detalis" можно снимать дамп и изучать создание импорта. Здесь я не буду приводить её полностью, а только самые важные моменты.

Код (Text):
  1.  
  2.  ;ecx - ImageBase библиотеки импортируемая функция
  3. WinLicen:00BD2395                 cmp     dword ptr [ebp+4BF002Dh], 1
  4. WinLicen:00BD239C                 jz      loc_BD23DB
  5. WinLicen:00BD23A2                 cmp     ecx, [ebp+xvKernel32Handle]
  6. WinLicen:00BD23A8                 jz      loc_BD23DB
  7. WinLicen:00BD23AE                 cmp     ecx, [ebp+xvUser32Handle]
  8. WinLicen:00BD23B4                 jz      loc_BD23DB
  9. WinLicen:00BD23BA                 cmp     ecx, [ebp+xvAdvapi32Handle]
  10. WinLicen:00BD23C0                 jz      loc_BD23DB
  11. WinLicen:00BD23C6
  12.  
  13.  ;Если ни один переход не сработал то функция запишется в IAT как есть
  14.  ;без переходника
  15. WinLicen:00BD23C6 _save_to_IATx1:                         ; CODE XREF: WinLicen:loc_BD2408
  16. WinLicen:00BD23C6                                         ; WinLicen:00BD242C ...
  17. WinLicen:00BD23C6                 lea     ebx, [ebp+xfiGetProcAddress]
  18. WinLicen:00BD23CC                 call    ebx
  19. WinLicen:00BD23CE
  20. WinLicen:00BD23CE _save_to_IATx0:                         ; CODE XREF: WinLicen:00BD245C
  21. WinLicen:00BD23CE                                         ; WinLicen:00BD2480 ...
  22. WinLicen:00BD23CE                 mov     edi, eax
  23. WinLicen:00BD23D0                 mov     [ebp+xvImportAddress], eax
  24. WinLicen:00BD23D6                 jmp     _save_to_IAT
  25. WinLicen:00BD23DB ; ---------------------------------------------------------------------------
  26. WinLicen:00BD23DB
  27. WinLicen:00BD23DB loc_BD23DB:                             ; CODE XREF: WinLicen:00BD239C
  28. WinLicen:00BD23DB                                         ; WinLicen:00BD23A8 ...
  29. WinLicen:00BD23DB                 lea     ebx, [ebp+xfiGetProcAddress]
  30. WinLicen:00BD23E1                 call    ebx
  31. WinLicen:00BD23E3                 cmp     dword ptr [ebp+4BF002Dh], 0
  32. WinLicen:00BD23EA                 jz      loc_BD240D      ; 00A9182Ch
  33. WinLicen:00BD23F0                 cmp     eax, [ebp+4BF25D1h] ; 00A91EB8h
  34. WinLicen:00BD23F6                 jz      loc_BD2408
  35. WinLicen:00BD23FC                 cmp     eax, [ebp+4BF01A1h] ; 00A8FA88h
  36. WinLicen:00BD2402                 jnz     loc_BD240D      ; 00A9182Ch
  37. WinLicen:00BD2408
  38. WinLicen:00BD2408 loc_BD2408:                             ; CODE XREF: WinLicen:00BD23F6
  39. WinLicen:00BD2408                 jmp     _save_to_IATx1
  40. WinLicen:00BD240D ; ---------------------------------------------------------------------------
  41.  
  42.  
  43.  ;Замена некоторых функций протекторными эмуляторами соответствующий функций
  44. WinLicen:00BD240D loc_BD240D:                             ; CODE XREF: WinLicen:00BD23EA
  45. WinLicen:00BD240D                                         ; WinLicen:00BD2402
  46. WinLicen:00BD240D                 cmp     eax, [ebp+xfExitProcess] ; 00A9182Ch
  47. WinLicen:00BD2413                 jnz     loc_BD2431
  48. WinLicen:00BD2419                 cmp     dword ptr [ebp+4BF0A6Dh], 0
  49. WinLicen:00BD2420                 jnz     loc_BD2431
  50. WinLicen:00BD2426                 lea     eax, [ebp+xfi_emul_ExitProcess] ; 00BBB01D
  51. WinLicen:00BD242C                 jmp     _save_to_IATx1
  52. WinLicen:00BD2431 ; ---------------------------------------------------------------------------
  53. WinLicen:00BD2431
  54. WinLicen:00BD2431 loc_BD2431:                             ; CODE XREF: WinLicen:00BD2413
  55. WinLicen:00BD2431                                         ; WinLicen:00BD2420
  56. WinLicen:00BD2431                 cmp     eax, [ebp+xfExitProcess]
  57. WinLicen:00BD2437                 jz      _save_to_IATx1
  58. WinLicen:00BD243D                 cmp     dword ptr [ebp+4BF0CF5h], 0
  59. WinLicen:00BD2444                 jz      loc_BD2461
  60. WinLicen:00BD244A                 cmp     eax, [ebp+xfReadFile] ; 00A900B0h
  61. WinLicen:00BD2450                 jnz     loc_BD2461
  62. WinLicen:00BD2456                 lea     eax, [ebp+xfi_emul_ReadFile]
  63. WinLicen:00BD245C                 jmp     _save_to_IATx0
  64. WinLicen:00BD2461 ; ---------------------------------------------------------------------------
  65. WinLicen:00BD2461
  66. WinLicen:00BD2461 loc_BD2461:                             ; CODE XREF: WinLicen:00BD2444
  67. WinLicen:00BD2461                                         ; WinLicen:00BD2450
  68. WinLicen:00BD2461                 cmp     dword ptr [ebp+4D3201Fh], 1
  69. WinLicen:00BD2468                 jnz     loc_BD2485
  70. WinLicen:00BD246E                 cmp     eax, [ebp+xfGetProcAddress] ; 00BD195Dh
  71. WinLicen:00BD2474                 jnz     loc_BD2485
  72. WinLicen:00BD247A                 lea     eax, [ebp+xfi_emul_GetProcAddress] ; 00BCCBA6
  73. WinLicen:00BD2480                 jmp     _save_to_IATx0
  74.  
  75. WinLicen:00BD2485
  76. WinLicen:00BD2485 loc_BD2485:                             ; CODE XREF: WinLicen:00BD2468
  77. WinLicen:00BD2485                                         ; WinLicen:00BD2474
  78. WinLicen:00BD2485                 xor     edi, edi
  79. WinLicen:00BD2487                 cmp     dword ptr [ebp+4BF1641h], 0
  80. WinLicen:00BD248E                 jz      loc_BD26A1
  81. WinLicen:00BD2494                 cmp     eax, [ebp+4D32062h] ; 00BD1949h
  82. WinLicen:00BD249A                 jnz     short loc_BD24A3 ; 00BD1951h
  83. WinLicen:00BD249C                 mov     eax, [ebp+4BF28FDh]
  84. WinLicen:00BD24A2                 inc     edi
  85.  
  86.  ;... пропустим мусор :)
  87.  
  88. WinLicen:00BD26A1 loc_BD26A1:                             ; CODE XREF: WinLicen:00BD248E
  89. WinLicen:00BD26A1                                         ; WinLicen:00BD2698
  90. WinLicen:00BD26A1                 or      edi, edi
  91. WinLicen:00BD26A3                 jz      loc_BD26AE      ; 00A905B8h
  92. WinLicen:00BD26A9                 jmp     _save_to_IATx0
  93. WinLicen:00BD26AE ; ---------------------------------------------------------------------------
  94.  
  95.  ;После мусора видна замена функции wsprintfA переходником, подробнее рассмотрим это
  96.  ;после в главе про макросы sdk
  97. WinLicen:00BD26AE loc_BD26AE:                             ; CODE XREF: WinLicen:00BD26A3
  98. WinLicen:00BD26AE                 cmp     eax, [ebp+xfwsprintf] ; 00A905B8h
  99. WinLicen:00BD26B4                 jnz     loc_BD26C5      ; 00A9185Ch
  100. WinLicen:00BD26BA                 lea     eax, [ebp+xfi_emul_wsprintf] ; 00B8CD09h
  101. WinLicen:00BD26C0                 jmp     _save_to_IATx0
  102. WinLicen:00BD26C5 ; ---------------------------------------------------------------------------
  103. WinLicen:00BD26C5
  104. WinLicen:00BD26C5 loc_BD26C5:                             ; CODE XREF: WinLicen:00BD26B4
  105. WinLicen:00BD26C5                 cmp     eax, [ebp+xfRaiseException] ; 00A9185Ch
  106. WinLicen:00BD26CB                 jnz     loc_BD26E9      ; 00BD1941h
  107. WinLicen:00BD26D1                 cmp     dword ptr [ebp+4D3201Fh], 1
  108. WinLicen:00BD26D8                 jnz     loc_BD26E9      ; 00BD1941h
  109. WinLicen:00BD26DE                 lea     eax, [ebp+xfi_emul_RaiseException] ; 00BCCB29h
  110. WinLicen:00BD26E4                 jmp     _save_to_IATx0
  111. WinLicen:00BD26E9 ; ---------------------------------------------------------------------------
  112. WinLicen:00BD26E9
  113. WinLicen:00BD26E9 loc_BD26E9:                             ; CODE XREF: WinLicen:00BD26CB
  114. WinLicen:00BD26E9                                         ; WinLicen:00BD26D8
  115. WinLicen:00BD26E9                 cmp     eax, [ebp+xfRtlEnterCriticalSection] ; 00BD1941h
  116. WinLicen:00BD26EF                 jz      loc_BD2701
  117. WinLicen:00BD26F5                 cmp     eax, [ebp+xfRtlLeaveCriticalSection] ; 00BD1945h
  118. WinLicen:00BD26FB                 jnz     loc_BD2706
  119. WinLicen:00BD2701
  120. WinLicen:00BD2701 loc_BD2701:                             ; CODE XREF: WinLicen:00BD26EF
  121. WinLicen:00BD2701                 jmp     _save_to_IATx0
  122. WinLicen:00BD2706 ; ---------------------------------------------------------------------------
  123. WinLicen:00BD2706
  124. WinLicen:00BD2706 loc_BD2706:                             ; CODE XREF: WinLicen:00BD26FB
  125. WinLicen:00BD2706                 mov     esi, 0
  126. WinLicen:00BD270B                 cmp     esi, 1
  127. WinLicen:00BD270E                 jnz     loc_BD2759
  128. WinLicen:00BD2714                 cmp     eax, [ebp+xfCreateThread] ; 00BD1935h
  129. WinLicen:00BD271A                 jnz     loc_BD272B      ; 00BD1939h
  130. WinLicen:00BD2720                 lea     eax, [ebp+xfi_emul_CreateThread]
  131. WinLicen:00BD2726                 jmp     _save_to_IATx0
  132. WinLicen:00BD272B ; ---------------------------------------------------------------------------
  133. WinLicen:00BD272B
  134. WinLicen:00BD272B loc_BD272B:                             ; CODE XREF: WinLicen:00BD271A
  135. WinLicen:00BD272B                 cmp     eax, [ebp+xfTerminateThread] ; 00BD1939h
  136. WinLicen:00BD2731                 jnz     loc_BD2742      ; 00BD193Dh
  137. WinLicen:00BD2737                 lea     eax, [ebp+xfi_emul_TerminateThread]
  138. WinLicen:00BD273D                 jmp     _save_to_IATx0
  139. WinLicen:00BD2742 ; ---------------------------------------------------------------------------
  140. WinLicen:00BD2742
  141. WinLicen:00BD2742 loc_BD2742:                             ; CODE XREF: WinLicen:00BD2731
  142. WinLicen:00BD2742                 cmp     eax, [ebp+xfExitThread] ; 00BD193Dh
  143. WinLicen:00BD2748                 jnz     loc_BD2759
  144. WinLicen:00BD274E                 lea     eax, [ebp+xfi_emul_ExitThread]
  145. WinLicen:00BD2754                 jmp     _save_to_IATx0
  146.  
  147.  ; ...
  148.  
  149.  ;Запись в IAT очередного элемента
  150. WinLicen:00BD294F _save_to_IAT:                           ; CODE XREF: WinLicen:00BD23D6
  151. WinLicen:00BD294F                 mov     esi, [ebp+MainImportHashArray]
  152. WinLicen:00BD2955                 lodsd
  153. WinLicen:00BD2956                 mov     dword ptr [esi-4], 0
  154. WinLicen:00BD295D                 rol     eax, 5
  155. WinLicen:00BD2960                 add     eax, 470EE6D2h
  156. WinLicen:00BD2965                 add     eax, [ebp+xvMainHandle]
  157. WinLicen:00BD296B                 mov     ecx, [ebp+xvImportAddress]
  158. WinLicen:00BD2971                 mov     [eax], ecx
  159. WinLicen:00BD2973                 lodsd
  160. WinLicen:00BD2974                 mov     dword ptr [esi-4], 0
  161.  
  162.  ;...
  163.  
  164.  ;сохранение переходов на импортируемые функции
  165. WinLicen:00BD29E1 loc_BD29E1:                             ; CODE XREF: WinLicen:00BD29D9
  166. WinLicen:00BD29E1                 push    eax
  167. WinLicen:00BD29E2                 cmp     dword ptr [ebp+4BF002Dh], 1
  168. WinLicen:00BD29E9                 jz      loc_BD2A0F
  169. WinLicen:00BD29EF                 mov     eax, 100h
  170. WinLicen:00BD29F4                 lea     ebx, [ebp+4CD12FDh]
  171. WinLicen:00BD29FA                 call    ebx
  172. WinLicen:00BD29FC                 cmp     eax, 50h ; 'P'
  173. WinLicen:00BD29FF                 jb      loc_BD2A0F
  174. WinLicen:00BD2A05                 mov     al, 90h ; 'Р'
  175.  
  176.  ;запись опкода инструкции перехода(jmp или call) на элемент импорта
  177.  ;с предшествующей инструкцией nop(инструкции call [mem32] и jmp [mem32]
  178.  ;занимают 6 байт, а непосредственные переходы - 5, поэтому протектор заменяя
  179.  ;инструкцию может поставить nop впереди)
  180. WinLicen:00BD2A07                 stosb
  181. WinLicen:00BD2A08                 pop     eax
  182. WinLicen:00BD2A09                 stosb
  183. WinLicen:00BD2A0A                 jmp     loc_BD2A26
  184. WinLicen:00BD2A0F ; ---------------------------------------------------------------------------
  185.  
  186.  ;запись того же опкода, но уже без nop
  187. WinLicen:00BD2A0F loc_BD2A0F:                             ; CODE XREF: WinLicen:00BD29E9
  188. WinLicen:00BD2A0F                                         ; WinLicen:00BD29FF
  189. WinLicen:00BD2A0F                 pop     eax
  190. WinLicen:00BD2A10                 stosb
  191. WinLicen:00BD2A11                 cmp     byte ptr [edi-1], 0E9h ; 'щ'
  192. WinLicen:00BD2A15                 jnz     loc_BD2A26
  193. WinLicen:00BD2A1B                 lea     ebx, [ebp+4CD12CDh]
  194. WinLicen:00BD2A21                 call    ebx
  195. WinLicen:00BD2A23                 mov     [edi+4], al
  196.  
  197.  ;Запись смещения
  198. WinLicen:00BD2A26 loc_BD2A26:                             ; CODE XREF: WinLicen:00BD2A0A
  199. WinLicen:00BD2A26                                         ; WinLicen:00BD2A15
  200. WinLicen:00BD2A26                 mov     eax, [ebp+xvImportAddress]
  201. WinLicen:00BD2A2C                 sub     eax, edi
  202. WinLicen:00BD2A2E                 sub     eax, 4
  203. WinLicen:00BD2A31                 stosd
  204.  

        В общем то если заменить 4 перехода nop'ами, и заодно устранить инструкцию stosb по адресу 0BD2A07h, то переходники генерироваться не будут, а также не будут смещаться непосредственные переходы на импорт, в результате чего получится чистая IAT и останется только переориентировать переходы. Но.. "An Error has ocurred while loading imports" - не стоит забывать про проверку CRC кода, создающего импорт. Здесь просматривается целых 2 цикла, но фактически мешается только один:

Код (Text):
  1.  
  2.  ;Загрузка в регистры адреса и размера проверяемого блока кода - 00BD19D9h, 1874h
  3. WinLicen:00BD2217                 lea     esi, [ebp+4D320F2h]
  4. WinLicen:00BD221D                 lea     edi, [ebp+4D33966h]
  5. WinLicen:00BD2223                 sub     edi, esi
  6.  
  7. WinLicen:00BD2225                 mov     edx, edi
  8. WinLicen:00BD2227                 mov     edi, [ebp+4BF0811h]
  9. WinLicen:00BD222D                 or      ecx, 0FFFFFFFFh
  10. WinLicen:00BD2230
  11.  
  12.  ;Цикл подсчёта хэша
  13. WinLicen:00BD2230 loc_BD2230:                             ; CODE XREF: WinLicen:00BD2240
  14. WinLicen:00BD2230                 xor     eax, eax
  15. WinLicen:00BD2232                 mov     al, [esi]
  16. WinLicen:00BD2234                 xor     al, cl
  17. WinLicen:00BD2236                 inc     esi
  18. WinLicen:00BD2237                 mov     eax, [edi+eax*4]
  19. WinLicen:00BD223A                 shr     ecx, 8
  20. WinLicen:00BD223D                 xor     ecx, eax
  21. WinLicen:00BD223F                 dec     edx
  22. WinLicen:00BD2240                 jnz     loc_BD2230
  23. WinLicen:00BD2246                 mov     eax, ecx
  24. WinLicen:00BD2248                 not     eax
  25.  
  26.  ;сравнения, инструкия по адресу 00BD2263h должна выполнится
  27.  ;чтоб ошибок при загрузке импорта не было
  28. WinLicen:00BD224A                 cmp     [ebp+4BF132Dh], eax
  29. WinLicen:00BD2250                 jz      loc_BD226D
  30. WinLicen:00BD2256                 cmp     dword ptr [ebp+4BF2865h], 0
  31. WinLicen:00BD225D                 jnz     loc_BD226D
  32. WinLicen:00BD2263                 mov     dword ptr [ebp+4BF0255h], 1
  33.  

        И вот, заменяя переход jz по адресу 00BD2250h на jmp получаем чистую IAT, но пока вместо ссылок на неё импорт вызывается напрямую, например:

Код (Text):
  1.  
  2. ___:00401E90                 jmp     near ptr 7756846Dh
  3. ___:00401E90 sub_401E90      endp
  4. ___:00401E90
  5. ___:00401E95 ; ---------------------------------------------------------------------------
  6. ___:00401E95                 dec     ebx
  7. ___:00401E96                 mov     eax, eax
  8. ___:00401E98
  9. ___:00401E98 ; --------------- S U B R O U T I N E ---------------------------------------
  10. ___:00401E98
  11. ___:00401E98
  12. ___:00401E98 sub_401E98      proc near               ; CODE XREF: sub_533024+88
  13. ___:00401E98                                         ; sub_534544+71 ...
  14. ___:00401E98                 jmp     near ptr 77520519h
  15. ___:00401E98 sub_401E98      endp
  16. ___:00401E98
  17. ___:00401E98 ; ---------------------------------------------------------------------------
  18. ___:00401E9D                 db 0C5h, 8Bh, 0C0h
  19. ___:00401EA0
  20. ___:00401EA0 ; --------------- S U B R O U T I N E ---------------------------------------
  21. ___:00401EA0
  22. ___:00401EA0
  23. ___:00401EA0 sub_401EA0      proc near               ; CODE XREF: sub_533024+A5
  24. ___:00401EA0                 jmp     near ptr 774F0326h
  25. ___:00401EA0 sub_401EA0      endp
  26. ___:00401EA0
  27. ___:00401EA5 ; ---------------------------------------------------------------------------
  28. ___:00401EA5                 das
  29. ___:00401EA6                 mov     eax, eax
  30. ___:00401EA8
  31. ___:00401EA8 ; --------------- S U B R O U T I N E ---------------------------------------
  32. ___:00401EA8
  33. ___:00401EA8
  34. ___:00401EA8 sub_401EA8      proc near               ; CODE XREF: sub_44CA24+8F
  35. ___:00401EA8                 jmp     near ptr 774ED024h
  36. ___:00401EA8 sub_401EA8      endp
  37.  

        Это конечно никуда не годиться, здесь должны находится ссылки на IAT. Поэтому в DLL есть код для восстановления, выполняться он будет после окончания распаковки(там где идёт переход на OEP, т.е. перед MessageBox'ом "Unpack finished!". Чтобы найти IAT, надо простассировать код после метки _save_to_IAT, когда MainImportHashArray будет известен. Взять этот MainImportHashArray можно здесь:

Код (Text):
  1.  
  2. WinLicen:00BD20E4                 mov     esi, [ebp+4BF00CDh]   ;Здесь находится этот адрес
  3. WinLicen:00BD20EA                 mov     ebx, [ebp+4BF0781h]
  4. WinLicen:00BD20F0                 mov     [ebp+MainImportHashArray], esi
  5.  

        Трассировать код можно отладчиком-эмулятором или в уме, что сложнее. Если в момент записи(адрес 00BD2971h), в eax будет адрес, непохожий на IAT, то можно переориентировать EIP на адрес 00BD2955h и трассировать снова, пока не увидим адрес 007CF618h - начало IAT. Размер можно определить, просмотрев дамп после запуска. Кончается IAT вот здесь: 007D24A0h. 7D24A0-7CF618 = 2E88h. И наконец после правки констант начала и размера IAT получаем полностью рабочий импорт, иногда распаковка на этом и заканчивается если тот, кто упаковывал программу не посмотрел SDK, которое позволяет не так уж и плохо защитить пользовательский код.

  Восстановление пользовательского кода(удаление макросов SDK):

        В примере используются три документированных макроса(CODE_ENCRYPT, CODE_CLEAR и CODE_REPLACE, см. include-файл из SDK для подробностей) и один недокументированный с использованием переходника wsprintf. CODE_ENCRYPT, CODE_CLEAR и wsprintf всего лишь расшифровывают впереди(а может и не впереди) лежащий код, для восстановления надо всего лишь передать управление на начало конструкции и перехватить управление после расшифровки. С CODE_REPLACE немного сложнее, придётся немного закопаться в код протектора, и даже столкнуться и обойти простенькую виртуальную машину.

        Начнём с wsprintf. Правда здесь совсем нет отличий от xprotector'а, но всё равно есть, что пояснить. Вот пример такой конструкции:

Код (Text):
  1.  
  2. ___:0055A960                 push    78263845h
  3. ___:0055A965                 push    5
  4. ___:0055A967                 push    0  ;действие 0 - расшифровка
  5. ___:0055A969                 push    0E3B90E0Ch
  6. ___:0055A96E                 push    0F478990Ah
  7. ___:0055A973                 push    78263845h
  8. ___:0055A978                 call    emul_wsprintfA
  9. ___:0055A97D                 add     esp, 18h
  10.  
  11.  ;... (зашифрованный кусок кода)
  12.  
  13. ___:0055AB04                 push    78263845h
  14. ___:0055AB09                 push    5
  15. ___:0055AB0B                 push    1 ;1 - шифровка
  16. ___:0055AB0D                 push    0E3B90E0Ch
  17. ___:0055AB12                 push    0F478990Ah
  18. ___:0055AB17                 push    78263845h
  19. ___:0055AB1C                 call    emul_wsprintfA
  20. ___:0055AB21                 add     esp, 18h
  21.  

        А теперь смотрим как удалять эти вставки:

Код (Text):
  1.  
  2. ;   ========== wsprintf recoverer ==========
  3.   ifdef wsprintf_in_IAT
  4.   ifdef wsprintf_emul_addr
  5.  
  6.  ;Сохраняем в регистре ebp правильный адрес wsprintf и помещаем туда адрес
  7.  ;своего кода, которые подменит в стеке адрес возврата и передаст
  8.  ;управление на функцию протектора xfi_emul_wsprintf
  9. mov ebp, DWORD PTR ds:[wsprintf_in_IAT]
  10. mov DWORD PTR ds:[wsprintf_in_IAT], offset @@wsprintf_redirect
  11.  
  12.  ;edi - начало секции кода, ebx - счётчик конструкций
  13. mov edi, CodeSectionVA
  14. xor ebx, ebx
  15.  
  16.  @@wsprintf_loop:
  17.  
  18.  ;Ищем(по сигнатуре) начальную(шифрующую)конструкцию,
  19.  ;если таких больше нет - выход из цикла
  20. invoke  find_wsprintf_decrypt, edi
  21. test    eax, eax
  22. jz  @@wsprintf_end
  23.  
  24.  ;Вывод сообщения в лог о том, что макрос wsprintf найден
  25. mov edi, eax
  26. invoke  wsprintfA, offset buffer1, offset fmt009, edi
  27. invoke  LogWrite, offset buffer1
  28.  
  29.  ;Передаём управление макросу - пусть расшифровывает всё что надо
  30. jmp edi ;decryption
  31.  
  32.  @@wsprintf_after_decryption:
  33.  
  34.  ;А сюда мы попадаем, потому что код около метки @@wsprintf_redirect
  35.  ;записывает в стек этот адрес
  36. add esp, 18h
  37. inc ebx
  38.  
  39.  ;Заменяем макрос nop'ами
  40. mov ecx, wsprintf_block_size
  41. mov al, 90h
  42. rep stosb
  43. jmp @@wsprintf_loop
  44.  
  45.  
  46.  @@wsprintf_end:
  47.  
  48.  ;Вывод в лог сообщения о количестве найденных макросов wsprintf
  49. invoke  wsprintf, offset buffer1, offset fmt011, ebx
  50. invoke  LogWrite, offset buffer1
  51.  
  52.  ;Записываем на место адрес истинной функции wsprintf,
  53.  ;переходник протектора больше не нужен
  54. mov DWORD PTR ds:[wsprintf_in_IAT], ebp
  55. mov edi, CodeSectionVA
  56.  
  57.  ;Далее идёт цикл по удалению завершающих конструкций макроса
  58.  @@wsprintf_clearloop:
  59. invoke  find_wsprintf_encrypt, edi
  60. test    eax, eax
  61. jz  @@wsprintf_clearend
  62.  
  63. mov edi, eax
  64. invoke  wsprintf, offset buffer1, offset fmt010, edi
  65. invoke  LogWrite, offset buffer1
  66.  
  67. mov ecx, wsprintf_block_size
  68. mov al, 90h
  69. rep stosb
  70. jmp @@wsprintf_clearloop
  71.  
  72.  ;Всё, макросов wsprintf больше не осталось
  73.  @@wsprintf_clearend:
  74.   endif
  75.   endif
  76.  

        wsprintf_in_IAT - можно найти после восстановления импорта imprec'ом, а wsprintf_emul_addr проскакивает в создании импорта, можно брать прямо оттуда. Теперь рассмотрим ещё два простеньких макроса - CODE_ENCRYPT и CODE_CLEAR:

Код (Text):
  1.  
  2.  
  3.  ;Начало у макросов CODE_ENCRYPT и CODE_REPLACE одинаковое:
  4. ___:0055B772                 call    near ptr unk_BD32FA
  5. ___:0055B772 ; ---------------------------------------------------------------------------
  6. ___:0055B777                 dd 0Ah ;Индекс для определения thread ID
  7. ___:0055B77B                 dd 0 ;decrypt
  8. ___:0055B77F                 dd 21h ;code size
  9. ___:0055B783                 db 20h ;какой-то байт(он не нужен)
  10. ___:0055B784 ; ---------------------------------------------------------------------------
  11.  
  12.  ;Скрытый пользовательский код
  13. ___:0055B784                 call    loc_5C079D
  14. ___:0055B789                 sub     edi, dword_7B8A2C
  15. ___:0055B78F                 mov     [ebp-4], edi
  16. ___:0055B792                 push    dword_77EF82
  17. ___:0055B798                 call    loc_55BDD2
  18. ___:0055B79D                 call    near ptr unk_BA772B
  19. ___:0055B79D ; ---------------------------------------------------------------------------
  20. ___:0055B7A2                 dd 0Ah ; -//-
  21. ___:0055B7A6                 dd 1 ;encrypt
  22. ___:0055B7AA                 dd 21h ; -//-
  23. ___:0055B7AE                 db 20h ; -//-
  24.  
  25.  ;В WinLicense demo нет макросов CODE_CLEAR, поэтому приведу такой макрос из
  26.  ;примера к SDK. sub_5ED4BF здесь и unk_BD32FA выше - одна и та же процедура
  27. ___:0040105D                 call    near ptr sub_5ED4BF
  28. ___:0040105D ; ---------------------------------------------------------------------------
  29. ___:00401062                 dd 8 ;thread ID index
  30. ___:00401066                 dd 0 ;action decrypt
  31. ___:0040106A                 dd 2Ah ;size
  32. ___:0040106E                 db 20h ;?
  33. ___:0040106F ; ---------------------------------------------------------------------------
  34.  
  35.  ;код пользователя
  36. ___:0040106F                 push    0
  37. ___:00401071                 push    offset aThemidaSdkEx_4 ; "Themida SDK example"
  38. ___:00401076                 push    offset aWeAreShowing_0 ; "We are showing this message inside a CL"...
  39. ___:0040107B                 push    0
  40. ___:0040107D                 call    sub_401106
  41.  
  42.  ;Затираем этот код пользователя, чтоб его не было видно в дизассемблере
  43.  ;после снятия дампа. Очевидно, что такой макрос подходит только для
  44.  ;кода, который должен выполнится всего один раз
  45. ___:00401082                 pusha
  46. ___:00401083                 call    $+5
  47. ___:00401088                 pop     edi
  48. ___:00401089                 sub     edi, 2Bh
  49. ___:0040108F                 mov     ecx, 2Bh
  50. ___:00401094                 xor     eax, eax
  51. ___:00401096                 rep stosb
  52. ___:00401098                 popa
  53.  

        При вызове процедуры расшифровки(или шифровки) поток уходит в бесконечный цикл, предварительно разморозив другой поток, который будет расшифровывать код, причём этот поток просто вынужден будет менять контекст(а конкретно регистр EIP в контексте) после обработки кода. Способ такой, передаём управление на начало макроса, не забыв перехватить native API функцию ZwSetContextThread. При вызове переориентируем EIP на продолжение цикла восстановление кода, после чего остаётся только заменить всё, что относиться к макросам nop'ами и ещё больше приблизится к моменту полной распаковки.

Код (Text):
  1.  
  2. ;   ========== CODE_ENCRYPT and CODE_CLEAR destroyer ==========
  3.   ifdef code_encrypt_proc
  4.  
  5.  ;Инициализация цикла
  6. mov edi, CodeSectionVA
  7.  
  8.  @@code_encrypt_loop:
  9.  
  10.  ;Поиск начала макроса, ищется по инструкции call code_encrypt_proc, это аналог
  11.  ;sub_5ED4BF из примера. Чтобы найти этот адрес, можно поискать например в hiew
  12.  ;вызовы(call'ы) процедур из секции кода программы в секцию протектора
  13. invoke  find_macro1_start, edi, code_encrypt_proc
  14. test    eax, eax
  15. jz  @@code_encrypt_end
  16.  
  17.  ;Вывод в лог сообшения о найденном макросе
  18. mov edi, eax
  19. invoke  wsprintf, offset buffer1, offset fmt012, edi
  20. invoke  LogWrite, offset buffer1
  21.  
  22.  ;code_encrypt_indicator - переменная, которую будет использовать перехваченная
  23.  ;функция ZwSetContextThread для того, чтобы определить, надо ли менять EIP или нет
  24. mov code_encrypt_indicator, 1
  25. mov gv0, edi
  26. mov gv1, ebx
  27.  
  28.  ;расшифровываем код
  29. jmp edi
  30.  
  31.  ;Сюда попадаем после расшифровки.
  32.  ;Обратите внимание на метку, она объявлена с двумя двоеточиями, это значит что к
  33.  ;ней можно обращаться из другой процедуры - особенность MASM'а
  34.  _code_encrypt_done::
  35. mov ebx, gv1
  36. mov edi, gv0
  37.  
  38.  ;Сброс индикатора для ZwSetContextThread
  39. mov code_encrypt_indicator, 0
  40.  
  41.  ;Читаем длину зашифрованного кода
  42. mov edx, [edi + 0Dh]
  43.  
  44.  ;Уничтожение начала макроса
  45. mov ecx, code_encrypt_size
  46. mov al, 90h
  47. rep stosb
  48.  
  49.  ;CODE_ENCRYPT?
  50.  ;(опкод первой инструкции окончания макроса CODE_ENCRYPT - E8, т.е. это call, см. пример)
  51. lea esi, [edi + edx]
  52. sub esi, 8
  53. cmp BYTE PTR [esi], 0E8h
  54. jnz @@code_encrypt_no_CE
  55.  
  56.  ;Вывод сообщения об уничтожении CODE_ENCRYPT,
  57.  ;а затем и само уничтожение
  58. invoke  wsprintf, offset buffer1, offset fmt013, edi, edx
  59. invoke  LogWrite, offset buffer1
  60.  
  61. mov edi, esi
  62. mov ecx, code_encrypt_size
  63. mov al, 90h
  64. rep stosb
  65. jmp @@code_encrypt_loop
  66.  
  67.  
  68.  @@code_encrypt_no_CE:
  69.  
  70.  ;Всё аналогично для удаления окончания CODE_CLEAR
  71. lea esi, [edi + edx]
  72. sub esi, code_clear_end_size
  73. cmp BYTE PTR [esi], 60h ;pusha
  74. jnz @@code_encrypt_no_CC
  75.  
  76. invoke  wsprintf, offset buffer1, offset fmt014, edi, edx
  77. invoke  LogWrite, offset buffer1
  78.  
  79. mov edi, esi
  80. mov ecx, code_clear_end_size
  81. mov al, 90h
  82. rep stosb
  83. jmp @@code_encrypt_loop
  84.  
  85.  @@code_encrypt_no_CC:
  86. jmp @@code_encrypt_loop
  87.  
  88.  @@code_encrypt_end:
  89.   endif
  90.  

        Результаты работы DLL можно посмотреть в IDA, перебирая адреса из лога, код должен восстановится правильно. Теперь остался только один вид макросов - CODE_REPLACE, но этот макрос представляет собой одну из самых мощных(если не самую мощную) защиту кода, макросу этому посвящается даже свой раздел :smile3:

  Восстановление кода после макросов CODE_REPLACE:

        Если вспомнить xprotector, то аналогичные макросы там выглядели так:

Код (Text):
  1.  
  2. jmp     invalid_opcode
  3. db      "xpro" ;Это просто сигнатура
  4. dd      0 ;0 - начало, 1 - окончание
  5. dd      0
  6. db      "xpro"
  7.  invalid_opcode:
  8.  

        Т.е. они выглядели почти также как и в sdk. Делаем предположение, что и в themid'е они также практически не меняются при упаковке. Смотрим SDK:

Код (Text):
  1.  
  2. CODEREPLACE_START MACRO
  3.  
  4.     jmp     @F
  5.  
  6.  ;Сигнатура уже здесь другая, WL - сокращение от WinLicense как я понимаю
  7.     db      'WL  ' ;
  8.     dd      ID_CODEREPLACE_START
  9.     dd      0
  10.     db      'WL  '
  11.  
  12.     @@:
  13.  
  14. ENDM    
  15.  
  16. CODEREPLACE_END MACRO
  17.  
  18.     jmp     @F
  19.  
  20.     db      'WL  '
  21.     dd      ID_CODEREPLACE_END
  22.     dd      0
  23.     db      'WL  '
  24.  
  25.     @@:
  26.  
  27. ENDM
  28.  

        Чтобы найти макрос CODE_REPLACE, достаточно поискать в шестнадцатиричном редакторе "WL ". Первый макрос находится по адресу 0558D23h. Однако испытания "древнего" способа с перехватам NtContinue заканчиваются неудачей. Да не просто неудачей а вообще непонятно чем, в лог не попадает исключение о неправильном опкоде, а это значит, что управление не попадает к векторному обработчику, чего просто не может быть, однако это так. Вспоминая, как Windows NT обрабатывает исключения, начинаем проверять функции, которые задействованы при этой обработке. Первая же функция этого ряда - KiUserExceptionDispatcher радует нас очень интересной инструкцией - jmp near ptr 0BCCA52h, причём адрес 0BCCA52h принадлежит конечно же протектору. Ничего не остаётся, кроме того, как туда заглянуть:

Код (Text):
  1.  
  2. WinLicen:00BCCA52 sub_BCCA52      proc near
  3. WinLicen:00BCCA52
  4. WinLicen:00BCCA52 pExceptionRecord= dword ptr  4
  5. WinLicen:00BCCA52 arg_4           = dword ptr  8
  6.  
  7.  ;Это недокументированный способ проверить версию ОС, WinNT перед нами или Win9x
  8. WinLicen:00BCCA52                 mov     cx, ds
  9. WinLicen:00BCCA55                 test    cl, 4
  10. WinLicen:00BCCA58                 jz      short @@WinNT
  11. WinLicen:00BCCA5A                 mov     ebx, [esp+arg_4]
  12. WinLicen:00BCCA5E                 mov     ecx, [esp+pExceptionRecord]
  13. WinLicen:00BCCA62                 jmp     short loc_BCCA6F
  14. WinLicen:00BCCA62 ; ---------------------------------------------------------------------------
  15. WinLicen:00BCCA64                 dd 0
  16. WinLicen:00BCCA68 ; ---------------------------------------------------------------------------
  17. WinLicen:00BCCA68
  18. WinLicen:00BCCA68 @@WinNT:                                ; CODE XREF: sub_BCCA52+6.j
  19. WinLicen:00BCCA68                 mov     ecx, [esp+pExceptionRecord]
  20. WinLicen:00BCCA6C                 mov     ebx, [esp+0]
  21. WinLicen:00BCCA6F
  22. WinLicen:00BCCA6F loc_BCCA6F:                             ; CODE XREF: sub_BCCA52+10.j
  23. WinLicen:00BCCA6F                 mov     eax, [ebx]
  24. WinLicen:00BCCA71                 call    $+5
  25. WinLicen:00BCCA76                 pop     ebp
  26. WinLicen:00BCCA77                 sub     ebp, 4D2D18Fh
  27. WinLicen:00BCCA7D                 pusha
  28.  
  29.  ;Проверка, произошло ли исключение внутри программы или скажем где-то в DLL
  30. WinLicen:00BCCA7E                 mov     eax, [ecx+CONTEXT.Eip]
  31. WinLicen:00BCCA84                 mov     edx, [ebp+xvImageSize]
  32. WinLicen:00BCCA8A                 add     edx, [ebp+xvHandle]
  33. WinLicen:00BCCA90                 cmp     eax, edx
  34. WinLicen:00BCCA92                 ja      short loc_BCCA9C
  35. WinLicen:00BCCA94                 cmp     eax, [ebp+xvHandle]
  36. WinLicen:00BCCA9A                 jnb     short loc_BCCAA4
  37. WinLicen:00BCCA9C
  38. WinLicen:00BCCA9C loc_BCCA9C:                             ; CODE XREF: sub_BCCA52+40.j
  39. WinLicen:00BCCA9C                 mov     eax, [ebp+4BF1AE9h]
  40. WinLicen:00BCCAA2                 jmp     short loc_BCCAFC
  41. WinLicen:00BCCAA4 ; ---------------------------------------------------------------------------
  42.  
  43.  ;Ожидание, пока IDT не окажется свободна(её могут использовать
  44.  ;другие потоки для своих ring0 дешифровщиков)
  45. WinLicen:00BCCAA4 loc_BCCAA4:                             ; CODE XREF: sub_BCCA52+48.j
  46. WinLicen:00BCCAA4                 mov     edx, 2
  47. WinLicen:00BCCAA9                 mov     eax, [ebp+4BF060Dh]
  48. WinLicen:00BCCAAF
  49. WinLicen:00BCCAAF @@waitforidt_loop:                      ; CODE XREF: sub_BCCA52+6D.j
  50. WinLicen:00BCCAAF                 xchg    dl, [eax]
  51. WinLicen:00BCCAB1                 or      dl, dl
  52. WinLicen:00BCCAB3                 jz      short loc_BCCAC1
  53. WinLicen:00BCCAB5                 pusha
  54. WinLicen:00BCCAB6                 push    1
  55. WinLicen:00BCCAB8                 call    dword ptr [ebp+Sleep]
  56. WinLicen:00BCCABE                 popa
  57. WinLicen:00BCCABF                 jmp     short @@waitforidt_loop
  58. WinLicen:00BCCAC1 ; ---------------------------------------------------------------------------
  59. WinLicen:00BCCAC1
  60. WinLicen:00BCCAC1 loc_BCCAC1:                             ; CODE XREF: sub_BCCA52+61.j
  61. WinLicen:00BCCAC1                 mov     ebx, [ecx+CONTEXT.Eip]
  62.  
  63.  ;Протектор держит таблицу с адресами исключений
  64. WinLicen:00BCCAC7                 call    find_exception_address
  65. WinLicen:00BCCACC                 or      eax, eax
  66.  
  67.  ;Переход, если адреса нету в таблице
  68. WinLicen:00BCCACE                 jz      short loc_BCCADB
  69. WinLicen:00BCCAD0                 mov     eax, [ebp+4BF060Dh]
  70. WinLicen:00BCCAD6                 mov     byte ptr [eax], 0
  71.  
  72.  ;И переход на реальную функцию KiUserExceptionDispatcher если исключение есть в таблице
  73. WinLicen:00BCCAD9                 jmp     short loc_BCCAFC
  74. WinLicen:00BCCADB ; ---------------------------------------------------------------------------
  75. WinLicen:00BCCADB
  76. WinLicen:00BCCADB loc_BCCADB:                             ; CODE XREF: sub_BCCA52+7C.j
  77. WinLicen:00BCCADB                 mov     eax, [ecx+CONTEXT.Eip]
  78. WinLicen:00BCCAE1                 mov     [ebp+xvOldEIP], eax
  79.  
  80.  ;Информация об исключении теряется, управление передаётся внутрь протектора, контекст
  81.  ;восстанавливается функцией NtContinue.
  82. WinLicen:00BCCAE7                 lea     eax, [ebp+xfExceptionHandlerProc]
  83. WinLicen:00BCCAED                 mov     [ecx+CONTEXT.Eip], eax
  84. WinLicen:00BCCAF3                 push    0
  85. WinLicen:00BCCAF5                 push    ecx
  86. WinLicen:00BCCAF6                 call    dword ptr [ebp+NtContinue]
  87. WinLicen:00BCCAFC
  88.  
  89.  ;Переход на функцию ОС, обрабатывающую исключение
  90. WinLicen:00BCCAFC loc_BCCAFC:                             ; CODE XREF: sub_BCCA52+50.j
  91. WinLicen:00BCCAFC                                         ; sub_BCCA52+87.j
  92. WinLicen:00BCCAFC                 mov     ax, ds
  93. WinLicen:00BCCAFF                 test    al, 4
  94. WinLicen:00BCCB01                 jz      short loc_BCCB13
  95. WinLicen:00BCCB03                 mov     eax, [ebp+4BF23E1h]
  96. WinLicen:00BCCB09                 add     eax, 8
  97. WinLicen:00BCCB0C                 add     eax, 42h ; 'B'
  98. WinLicen:00BCCB11                 jmp     short loc_BCCB19
  99. WinLicen:00BCCB13 ; ---------------------------------------------------------------------------
  100. WinLicen:00BCCB13
  101. WinLicen:00BCCB13 loc_BCCB13:                             ; CODE XREF: sub_BCCA52+AF.j
  102. WinLicen:00BCCB13                 lea     eax, [ebp+4D218E3h]
  103. WinLicen:00BCCB19
  104. WinLicen:00BCCB19 loc_BCCB19:                             ; CODE XREF: sub_BCCA52+BF.j
  105. WinLicen:00BCCB19                 lea     ebx, [ebp+4D2D23Ch]
  106. WinLicen:00BCCB1F                 mov     [ebx+1], eax
  107. WinLicen:00BCCB22                 popa
  108. WinLicen:00BCCB23                 push    12345678h
  109. WinLicen:00BCCB28                 retn
  110. WinLicen:00BCCB28 sub_BCCA52      endp ; sp = -0Ch
  111.  

        Исходя из вышестоящего кода, обработка исключения передаётся операционной системе, если адреса этого исключения нет в какой-то внутренней таблице процессора, при этом информация об исключении теряется. Могут быть два случая - произошло обычное исключение, или управление попало на макрос CODE_REPLACE. Очевидно, что в первом случае управление должно вернуться на адрес, вызвавший исключение, а потом опять на 00BCCA52h, и уже потом на KiUserExceptionDispatcher, при этом адрес должен был быть занесён в таблицу. Во втором случае просто выполнится код, который протектор вытащил изнутри макроса, и управление после этого попадёт за пределы макроса. Посмотрим на код xfExceptionHandlerProc, хотя толку от этого мало будет :smile3:

Код (Text):
  1.  
  2.  ;Код этот так сильно разбавлен мусором, что без плагина невозможно вообще понять
  3.  ;что к чему. С плагином идёт описание, как удалять мусорные конструкции,
  4.  ;здесь оно точно уж пригодится
  5. WinLicen:00BBBE5F                 pushf
  6. WinLicen:00BBBE60                 pusha
  7. WinLicen:00BBBE61                 call    $+5
  8. WinLicen:00BBBE66                 pop     ebp
  9. WinLicen:00BBBE67                 sub     ebp, 4D1C57Fh
  10. WinLicen:00BBC05F                 lea     eax, [ebp+4D1DC64h]
  11. WinLicen:00BBC2CC                 push    eax
  12. WinLicen:00BBC2D0                 mov     [esp], eax
  13. WinLicen:00BBC2F0                 cmp     dword ptr [ebp+4BF2961h], 0
  14. WinLicen:00BBC2F7                 jz      loc_BBC5D4
  15. WinLicen:00BBC5A1                 push    0FFFFFFFFh
  16. WinLicen:00BBC5C8                 call    dword ptr [ebp+4BF2851h]
  17. WinLicen:00BBC5CE                 mov     edi, [ebp+4BF013Dh]
  18. WinLicen:00BBC5D4
  19. WinLicen:00BBC5D4 loc_BBC5D4:                             ; CODE XREF: WinLicen:00BBC2F7.j
  20.  
  21.  ;...
  22.  ;Весь смысл внутри этого call'а, можно посмотреть и туда
  23. WinLicen:00BBCD50                 call    eax
  24. WinLicen:00BBCD52                 mov     ebx, [ebp+4BF1C7Dh]
  25. WinLicen:00BBCD58                 mov     ecx, [ebp+4BF060Dh]
  26. WinLicen:00BBCF87                 mov     byte ptr [ecx], 0
  27. WinLicen:00BBCF9C                 lea     ecx, [ebp+4D1DC64h]
  28. WinLicen:00BBD21F                 mov     [ecx+1], eax
  29. WinLicen:00BBD2EB                 add     esp, 4
  30. WinLicen:00BBD549                 popa
  31. WinLicen:00BBD54A                 popf
  32. WinLicen:00BBD54B                 push    12345678h
  33. WinLicen:00BBD550                 retn
  34.  
  35.  ...
  36.  
  37.  ;Я уже упоминал о виртуальной машине, именно здесь она и находится
  38.  ;Исследовать у меня желание не возникло, но одно её присутствие уже есть
  39.  ;нехороший знак.
  40. WinLicen:00B1112E sub_B1112E      proc near
  41. WinLicen:00B1112E
  42. WinLicen:00B1112E arg_0           = dword ptr  4
  43. WinLicen:00B1112E arg_4           = dword ptr  8
  44. WinLicen:00B1112E
  45. WinLicen:00B1112E                 pusha
  46. WinLicen:00B11148                 mov     esi, [esp+20h+arg_0]
  47. WinLicen:00B1114C                 xor     [ebp+4BF168Dh], edi
  48. WinLicen:00B11152                 mov     ebx, [esp+20h+arg_4]
  49. WinLicen:00B11158                 mov     edi, [ebp+4C697FCh]
  50. WinLicen:00B1115E                 mov     [ebp+4BF070Dh], ecx
  51. WinLicen:00B11164                 mov     edx, [ebp+4C6980Ch]
  52. WinLicen:00B11176                 mov     dword ptr [ebp+4C6981Ch], 0
  53. WinLicen:00B1118A                 mov     dword ptr [ebp+4C69820h], 0
  54. WinLicen:00B111C3                 mov     eax, 1Fh
  55. WinLicen:00B111DB                 shl     eax, 2
  56. WinLicen:00B1121A                 mov     ecx, [ebp+4C69828h]
  57. WinLicen:00B11220                 mov     [ebp+4BF0A25h], eax
  58. WinLicen:00B11226                 mov     [eax+edx], ecx
  59. WinLicen:00B1122A                 mov     [ebp+4BF1DE9h], edi
  60. WinLicen:00B11230                 lea     eax, [eax+edx]
  61. WinLicen:00B11255                 mov     [ebp+4C69824h], eax
  62. WinLicen:00B11267                 jmp     loc_B1133C
  63. WinLicen:00B1126C ; ---------------------------------------------------------------------------
  64. WinLicen:00B1126C                 mov     [ebp+4BF29E9h], eax
  65. WinLicen:00B11272
  66. WinLicen:00B11272 loc_B11272:                             ; CODE XREF: sub_B1112E+210.j
  67. WinLicen:00B11272                 movzx   eax, byte ptr [esi]
  68. WinLicen:00B1128E                 shl     eax, 2
  69. WinLicen:00B112B0                 call    dword ptr [eax+edi]
  70. WinLicen:00B112B3                 mov     [ebp+4BF1D81h], edi
  71. WinLicen:00B112B9                 inc     dword ptr [ebp+4C69820h]
  72. WinLicen:00B112CB                 mov     eax, [ebp+4C69820h]
  73. WinLicen:00B112F9                 mov     ecx, 0Ah
  74. WinLicen:00B11316                 mul     cl
  75. WinLicen:00B11329                 mov     esi, [esp+20h+arg_0]
  76. WinLicen:00B11334                 add     esi, eax
  77. WinLicen:00B11336                 sub     [ebp+4BF0571h], eax
  78. WinLicen:00B1133C
  79. WinLicen:00B1133C loc_B1133C:                             ; CODE XREF: sub_B1112E+139.j
  80. WinLicen:00B1133C                 cmp     esi, ebx
  81. WinLicen:00B1133E                 jb      loc_B11272
  82. WinLicen:00B11345                 popa
  83. WinLicen:00B11346                 sub     [ebp+4BF1BBDh], ebx
  84. WinLicen:00B1134C                 retn    8
  85. WinLicen:00B1134C sub_B1112E      endp
  86.  

        Практически можно сдаваться, ведь легкое восстановление кода из CODE_REPLACE не представляется возможным, но..

Код (Text):
  1.  
  2. WinLicen:00BCCADB                 mov     eax, [ecx+CONTEXT.Eip]
  3. WinLicen:00BCCAE1                 mov     [ebp+xvOldEIP], eax ;где xvOldEIP = 4BF14B9h
  4.  

        Если код проверки на принадлежность адреса исключения к CODE_REPLACE лежит в открытом виде, то HEX-редакторе можно будет найти DWORD 4BF14B9h. И действительно, найти его можно и не раз. Первое вхождение 7CCAE3 выводит нас на перехваченную функцию KiUserExceptionDispatcher, но уже следующее выводит на совсем другой код. Он также очень сильно разбавлен мусором, поэтому придётся несколько минут посидеть, прежде чем раскопать окрестности вхождения и выделить нужный код, который обслуживает макрос(т.е. сверяет адрес из xvOldEIP с таблицей адресов, где применены макросы). Смотрим на этот код, на этот раз толку будет хоть отбавляй:

Код (Text):
  1.  
  2.  ;Вот где она, таблица-то..
  3. WinLicen:00BE0848                 lea     edi, [ebp+xaCodeReplaceTable]
  4. WinLicen:00BE0854                 add     edi, 5
  5. WinLicen:00BE0857                 mov     edx, ebx
  6.  
  7.  ;Записываем в переменную адрес таблицы
  8. WinLicen:00BE0931                 mov     [ebp+xvCodeReplaceTable], edi
  9. WinLicen:00BE095E                 lea     ecx, [ebp+xaCodeReplaceTableEnd]
  10. WinLicen:00BE0988                 sub     ecx, edi
  11. WinLicen:00BE0BE1                 shr     ecx, 2
  12. WinLicen:00BE0E45                 inc     ecx
  13. WinLicen:00BE0E46                 sub     edx, 4A202D45h
  14. WinLicen:00BE0F40                 mov     [ebp+4D3D93Bh], ecx
  15. WinLicen:00BE11AB                 mov     edx, [ebp+4BF039Dh]
  16.  
  17.  ;Таблица лежит в зашифрованном виде, нужны два ключа, чтоб запустить
  18.  ;"мощный" криптоалгоритм для её расшифровки
  19. WinLicen:00BE11B1                 mov     eax, [ebp+xvCodeReplaceKey1]
  20. WinLicen:00BE11D6                 mov     ebx, [ebp+xvCodeReplaceKey2]
  21. WinLicen:00BE1307                 call    _codereplace_decrypt
  22.  
  23.  ;Получаем RVA адреса исключения и ищем его в таблице
  24. WinLicen:00BE14F5                 mov     ebx, [ebp+xvOldEIP]
  25. WinLicen:00BE177F                 sub     ebx, [ebp+xvHandle]
  26. WinLicen:00BE1A01                 mov     esi, [ebp+xvCodeReplaceTable]
  27. WinLicen:00BE1A07                 mov     edx, 4BBF4CCEh
  28. WinLicen:00BE1A0C                 lea     edi, [ebp+xaCodeReplaceTableEnd]
  29. WinLicen:00BE1A12                 push    ebx
  30. WinLicen:00BE1A13                 mov     dx, 1246h
  31. WinLicen:00BE1A17                 pop     edx
  32. WinLicen:00BE1A18                 call    _codereplace_find
  33. WinLicen:00BE1C37                 or      eax, eax
  34.  
  35.  ;Если исключение никак не связано с CODE_REPLACE, то вот переход выполнится
  36. WinLicen:00BE1C39                 jz      _no_codereplace
  37.  
  38.  ;Расчёт адреса для перехода на краденный из макроса код,
  39.  ;Видно, что в таблице третий элемент это RVA - CodeReplaceConst1
  40. WinLicen:00BE1C5B                 mov     eax, [esi+8]
  41. WinLicen:00BE1C7E                 sub     ebx, [esi]
  42. WinLicen:00BE1D32                 add     eax, ebx
  43. WinLicen:00BE1D3C                 add     eax, [ebp+xvCodeReplaceConst1]
  44. WinLicen:00BE1DEF                 add     eax, [ebp+xvHandle]
  45.  
  46.  ;Запись нового адреса для передачи управления
  47. WinLicen:00BE207F                 mov     [ebp+xvOldEIP], eax
  48. WinLicen:00BE208E                 jmp     loc_BE258E
  49.  
  50.  ;Если это не макрос, то xvOldEIP не меняется, управление попадёт туда для
  51.  ;повторной обработки исключения протектором
  52. WinLicen:00BE2096 _no_codereplace:                        ; CODE XREF: WinLicen:00BE1C39.j
  53. WinLicen:00BE2096                 mov     ebx, [ebp+xvOldEIP]
  54. WinLicen:00BE22A6                 call    loc_BE423D
  55.  

        После такого описание думаю понятно что делать с краденным кодом, достаточно найти таблицу, расщифровать её, и просто скопировать код на место, не забыв подправить относительные смещения для длинных переходов. Другие способы, основанные на поиске макросов по сигнатуре "WL " недееспособны, т.к. при упаковке есть такая опция, как автоматическое создание CODE_REPLACE макросов, но вот в таблице они будут все, вне зависимости от того, автомитически ли они были созданы или нет. Теперь пара слов о восстановлении:

Код (Text):
  1.  
  2.  ;CODE_REPLACE constants
  3. code_replace_table  equ     00BDD184h ;адрес таблицы
  4. code_replace_n      equ     0Ch ;число макросов
  5. code_replace_key1   equ     00BDD226h ;первый ключ ..
  6. code_replace_key2   equ     00BDD22Ah ;.. и второй ключ
  7. code_replace_const1 equ     0FB8E7h ;константа для суммирования с 3-им элементом таблицы
  8. code_replace_decrypt    equ     00AC9B3Dh ;функция расшифровки таблицы
  9. code_replace_macrosize  equ     12h ;размер начала макроса из SDK
  10.  
  11.  ;...
  12.  
  13. ;   ========== CODE_REPLACE destroyer ==========
  14.   ifdef code_replace_table
  15.   ifdef code_replace_n
  16.   ifdef code_replace_key1
  17.   ifdef code_replace_key2
  18.   ifdef code_replace_const1
  19.   ifdef code_replace_decrypt
  20.  
  21.  ;Расшифровываем таблицу
  22. mov edi, code_replace_table
  23. mov eax, code_replace_key1
  24. mov eax, [eax]
  25. mov ebx, code_replace_key2
  26. mov ebx, [ebx]
  27. mov ecx, code_replace_n
  28. imul    ecx, 3
  29.  
  30. mov ebp, code_replace_decrypt
  31. call    ebp
  32.  
  33.  ;Начинается цикл копирования и правки кода
  34. mov ebp, code_replace_table
  35. mov ebx, code_replace_n
  36.  
  37.  @@code_replace_loop:
  38.  
  39.  ;Первый элемент в таблице - RVA макроса в коде, второй - RVA конца
  40.  ;макроса, третий - RVA адреса, куда протектор спрятал украденный код
  41. mov esi, [ebp+8]
  42. add esi, code_replace_const1
  43. add esi, MHandle
  44.  
  45. mov edi, [ebp]
  46. add edi, MHandle
  47.  
  48. mov ecx, [ebp+4]
  49. add ecx, MHandle
  50. sub ecx, edi
  51.  
  52. push    ecx
  53. push    esi
  54. push    edi
  55.  
  56.  ;сначала копирование
  57. rep movsb
  58.  
  59. pop edi
  60. pop esi
  61. pop ecx
  62.  
  63.  ;теперь правда смещений
  64. invoke  code_replace_fix, edi, esi, ecx
  65.  
  66.  ;А потом удаление всяких сигнатур "WL  ", в общем
  67.  ;удаление всего лишнего
  68. cmp DWORD PTR [edi - 4], 20204C57h
  69. jnz @@code_replace_iter
  70.  
  71. push    edi
  72. push    ecx
  73. push    ecx
  74.  
  75. mov al, 90h
  76. mov ecx, code_replace_macrosize
  77. sub edi, ecx
  78. rep stosb
  79.  
  80. pop ecx
  81. add edi, ecx
  82. mov ecx, code_replace_macrosize
  83. rep stosb
  84.  
  85. pop ecx
  86. pop edi
  87.  
  88.  ;Вывод в лог и итерация цикла
  89.  @@code_replace_iter:
  90. invoke  wsprintf, offset buffer1, offset fmt015, edi, ecx
  91. invoke  LogWrite, offset buffer1
  92.  
  93. add ebp, 0Ch
  94. dec ebx
  95. jnz @@code_replace_loop
  96.   endif
  97.   endif
  98.   endif
  99.   endif
  100.   endif
  101.   endif
  102.  

        Всё, все ступени защиты преодолены, здесь можно закончить распаковку любой программы, за исключением творений Oreans. Программы, упакованные распакованной WinLicense не хотят работать, выдают исключения. Защита эта встроена в логику работы программы, это сильно усложняет её поиск, но не исключает совсем.

  Тест на упакованность(доп. защита от авторов протектора):

        Исследование приведу в кратком изложении. Вновь запакованный calc.exe не работает, вылетает с исключением обращения к памяти. Если запускать его через лоадер, то будет видно, в логе будут видны вызовы VirtualAlloc. Перехватывая и снимая дамп сразу после последнего вызова VirtualAlloc перед исключением, быстро находим инструкцию, приводящую к краху:

Код (Text):
  1.  
  2. WinLicen:0121F163                 push    4
  3. WinLicen:0121F165                 push    1000h
  4. WinLicen:0121F16A                 push    dword ptr [ebp+7490999h]
  5. WinLicen:0121F170                 push    0
  6. WinLicen:0121F172                 call    eax
  7. WinLicen:0121F174                 test    eax, eax
  8. WinLicen:0121F176                 jnz     loc_121F189
  9. WinLicen:0121F17C                 mov     eax, 0
  10. WinLicen:0121F181                 lea     ecx, [ebp+7498F7Eh]
  11. WinLicen:0121F187                 jmp     ecx
  12. WinLicen:0121F189 ; ---------------------------------------------------------------------------
  13. WinLicen:0121F189
  14. WinLicen:0121F189 loc_121F189:                            ; CODE XREF: WinLicen:0121F176.j
  15. WinLicen:0121F189                 mov     ecx, eax
  16. WinLicen:0121F18B                 mov     eax, ebx
  17. WinLicen:0121F18D                 add     eax, [eax+3Ch]
  18. WinLicen:0121F190                 add     eax, 0F8h
  19. WinLicen:0121F195                 mov     edx, [eax+0Ch]
  20. WinLicen:0121F198                 add     edx, ebx
  21. WinLicen:0121F19A                 cmp     dword ptr [ebp+74910A1h], 0
  22. WinLicen:0121F1A1                 jz      loc_121F1B5
  23. WinLicen:0121F1A7                 mov     ebx, [ebp+74910A1h]
  24. WinLicen:0121F1AD                 mov     eax, [ebp+7491D61h]
  25. WinLicen:0121F1B3                 mov     [ebx], eax
  26. WinLicen:0121F1B5
  27. WinLicen:0121F1B5 loc_121F1B5:                            ; CODE XREF: WinLicen:0121F1A1.j
  28. WinLicen:0121F1B5                 push    ecx
  29. WinLicen:0121F1B6                 push    edx
  30.  
  31.  ;Смещение относительно EBP не изменилось, но если вспомнить xprotector,
  32.  ;то там игнорировались все смещения
  33. WinLicen:0121F1B7                 lea     eax, [ebp+5A6E80h]
  34. WinLicen:0121F1BD                 call    eax
  35.  

        Пользуясь тем, что код это лежит в открытом виде, находим его в дампе по адресу 005A7171. По перекрестным ссылкам можно найти код, который учавствует в упаковке и как раз обрабатывает такие куски:

Код (Text):
  1.  
  2.  
  3.  ;Видим кучки nop'ов оставшиеся после удаления макросов wsprintf
  4. ___:005A7227                 nop
  5. ___:005A7228                 nop
  6. ___:005A7229                 nop
  7. ___:005A722A                 nop
  8. ___:005A722B                 nop
  9. ___:005A722C                 nop
  10. ___:005A722D                 nop
  11. ___:005A722E                 cmp     [ebp+arg_4], 0
  12. ___:005A7232                 jnz     short loc_5A7251
  13. ___:005A7234                 mov     eax, offset loc_5A70BF
  14. ___:005A7239                 mov     ebx, offset sub_5A7205
  15. ___:005A723E                 push    ebx
  16. ___:005A723F                 push    eax
  17. ___:005A7240                 call    sub_5BFFE7
  18. ___:005A7245                 mov     dword_78D32F, 0
  19. ___:005A724F                 jmp     short loc_5A72AD
  20. ___:005A7251 ; ---------------------------------------------------------------------------
  21. ___:005A7251
  22. ___:005A7251 loc_5A7251:                             ; CODE XREF: sub_5A7205+2D.j
  23. ___:005A7251                 push    0
  24. ___:005A7253                 call    sub_5C0881
  25. ___:005A7258                 push    0
  26. ___:005A725A                 call    sub_5C1F49
  27. ___:005A725F                 push    0
  28. ___:005A7261                 call    sub_5C1F28
  29. ___:005A7266                 push    0
  30. ___:005A7268                 push    0
  31. ___:005A726A                 push    0
  32. ___:005A726C                 push    64h
  33. ___:005A726E                 call    sub_5C091B
  34. ___:005A7273                 call    sub_5C2538
  35. ___:005A7278                 call    sub_5C2549
  36. ___:005A727D                 call    sub_5C2551
  37. ___:005A7282                 call    sub_5C2559
  38. ___:005A7287                 call    sub_5C2561
  39. ___:005A728C                 call    sub_5C2579
  40. ___:005A7291                 call    sub_5C2571
  41. ___:005A7296                 mov     eax, offset loc_5A70BF
  42. ___:005A729B                 mov     ebx, offset sub_5A7205
  43. ___:005A72A0                 push    [ebp+arg_0]
  44. ___:005A72A3                 push    ebx
  45. ___:005A72A4                 push    eax
  46. ___:005A72A5                 call    sub_5C01D7
  47.  

        Как и в xprotector'е здесь тоже поддерживается массив для обработки перемещаемых элементов(как relocations в DLL). Функция 5C01D7h как раз заполняет этот массив, куда добавляются все адреса внутри обрабатываемого блока(в данном случае от 5A70BFh до 5A7205h). Но наше необработанное(5A6E80h) смещение в данный блок не попадает, попадает оно в другой блок, обработка которого ведётся в процедуре по адресу 5A6FCFh, но оказывается управление вообще не попадает на этот адрес! Попадать оно туда естесственно должно, значит защита где-то рядом. Протектор при упаковке собирает адреса всех таких блоков в единый массив и по очереди их вызывает по два раза, один раз для инициализации, второй для создания релоков(могу ошибаться), и адрес 5A6FCFh почему-то не попадает в этот массив. По xref'ам выходим на такой код:

Код (Text):
  1.  
  2. ___:0055C309 loc_55C309:                             ; CODE XREF: sub_55BE60+49D
  3. ___:0055C309                 mov     al, 9
  4. ___:0055C30B                 and     eax, 0FFh
  5. ___:0055C310                 add     eax, ebx
  6. ___:0055C312                 sub     eax, edx
  7. ___:0055C314                 imul    eax, ecx
  8.  
  9.  ;Вызывается вот эта интересная функция..
  10. ___:0055C317                 call    sub_5C1B38
  11. ___:0055C31C                 or      eax, eax
  12.  
  13.  ;И если функция возвращает 1, то адрес блока не передаётся в функцию,
  14.  ;да и сама функция вообще не вызывается
  15. ___:0055C31E                 jnz     short loc_55C33E
  16. ___:0055C320                 push    offset sub_5BEDDD
  17. ___:0055C325                 call    sub_55B63C
  18. ___:0055C32A                 push    offset sub_5A6FCF
  19. ___:0055C32F                 call    sub_55B63C
  20. ___:0055C334                 push    offset sub_5A6181
  21. ___:0055C339                 call    sub_55B63C
  22. ___:0055C33E
  23. ___:0055C33E loc_55C33E:                             ; CODE XREF: sub_55BE60+4BE
  24. ___:0055C33E                 cmp     dword_7B8254, 0
  25. ___:0055C345                 jz      short loc_55C371
  26.  
  27.  ...
  28.  
  29.  ;Смотрим теперь что там за функция такая вызывается:
  30. ___:005C1B38 sub_5C1B38      proc near               ; CODE XREF: sub_558B00+12E.p
  31. ___:005C1B38                                         ; sub_55BE60+4B7.p
  32. ___:005C1B38                 call    GetThreadsCount
  33. ___:005C1B3D                 cmp     eax, 20
  34. ___:005C1B40                 jbe     short loc_5C1B49
  35. ___:005C1B42                 mov     eax, 1
  36. ___:005C1B47                 jmp     short locret_5C1B4E
  37. ___:005C1B49 ; ---------------------------------------------------------------------------
  38. ___:005C1B49
  39. ___:005C1B49 loc_5C1B49:                             ; CODE XREF: sub_5C1B38+8.j
  40. ___:005C1B49                 mov     eax, 0
  41. ___:005C1B4E
  42. ___:005C1B4E locret_5C1B4E:                          ; CODE XREF: sub_5C1B38+F.j
  43. ___:005C1B4E                 retn
  44. ___:005C1B4E sub_5C1B38      endp
  45.  

        Вот где корень зла :smile3: Функция возвращает единицу, если количество тредов больше 20 и 0 если меньше. Если вспомнить, сколько тредов создаёт протектор при своей работе, то становится понятно, что функция вернёт 1 если WinLicense запустили в упакованном виде и 0, если запустили дамп. После патча, который сделает, чтобы эта функция всегда возвращает 1, прогамма WinLicense не узнает, что её распаковали и будет запаковывать всё подряд и без ошибок, что собственно от ней и требуется. Наконец после этого патча протектор оказывается побеждённым полностью, уррааа!! :smile3:

  Послесловие:

        Хочется предупредить, но похоже это последний случай, когда themida снималась так легко, уже после написания статьи автор обновил своё творение, и среди обновлений появился новый макрос, по сравнению с которым приведённый выше CODE_REPLACE отдыхает. Новинка превращает пользовательский код в псевдо-код для виртульной машины, это уже очень много говорит о силе защиты. Вообще будушее защиты именно за виртуальными машинами и обфускацией кода, и пора думать как с этим бороться.. На этом пожалуй всё.

Исходники и инструменты: здесь © CrystalDragon

0 1.763
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532