Защита программы Ulead COOL 3D v3.5 (trial)

Дата публикации 13 апр 2003

Защита программы Ulead COOL 3D v3.5 (trial) — Архив WASM.RU

Что собираемся делать ?

  Объект исследования: программа Ulead COOL 3D, триальная защита

  Инструменты: Soft-Ice (под '98), немного мозгов

  Цель: изучение win32-программ, обладающих защитой от отладки

  Примечание: ничего особо нового, простая техника, SEH, etc...

  Ниже я собираюсь исследовать защиту этой проги, которая позволяет пользователю использовать ее функциональность только в течении календарного месяца (30 дней) или купить возможность ее использования неограниченно во времени (вид входного окошка после инсталляции, инсталлятор UC3D35TBYB_E.exe):

 

  Что она вообще делает ? Примерно - это какой-то графический редактор (вид после нажатия кнопки "TRY"):

 

 Таким образом в первом приближении сразу после инсталляции данная программа позволяет полноценно работать, но в стартовом окошке напоминает о том, что до конца использования осталось 30 дней ("Remaining Days - 30") и предлагает, видимо, купить разрешение на полное использование ("BUY").

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

 

 Ага, программа предупреждает нас, что осталось на 1 день меньше. Если перевести дату ровно на месяц вперед, то кнопка "TRY" становится недоступна и число оставшихся дней равно 0:

 

  Кнопка "TRY" недоступна. Попробуем поймать программу на том, как она закрывает для доступа кнопку и поисследовать, чем она при этом руководствуется. Win32-приложение обычно делает это при помощи API-функции EnableWindow:

 EnableWindow(Id_Объекта_Окна,0/1-закрыть/открыть Объект)

 Устанавливаем на машине дату таковой, чтобы программа решила, что осталось 0 дней для использования, устанавливаем breakpoint в SoftIce:

 bpx EnableWindow

  запускаем программу, получаем break и видим в отладчике место вызова (оказалось, код принадлежит некоей xsystem.dll, которую можно найти в том каталоге, куда инсталлировалась программа Ulead COOL 3D):

Код (Text):
  1.  
  2. 026D7F5F:
  3.   mov  edi,User32!EnableWindow
  4.   push edi
  5.   mov  edi,esp
  6.   pushad
  7.   lea  esi,[ebp+2C63h]
  8.   lodsb
  9.   cmp  al,90h
  10.   jnz  026D7F8A
  11.   push 03E8h
  12.   push dword ptr [edi+8]
  13.   call [ebp+2B50h]
  14.   push 0 ; FALSE - disable button "TRY"
  15.   push eax
  16.   <strong>call [ebp+2B54h] ; Точка вызова EnableWindow</strong>
  17. 026D7F8A:
  18.   popad
  19.   ...
  20. </code></pre>
  21.  
  22. <p>&nbsp; Итак, программа анализирует первый байт некоторой структуры
  23. (lea esi,[ebp+2C63h], lodsb, cmp al,90h), сравнивает его с 90h, если он
  24. оказывается таковым, то кнопка "TRY" становится недоступной (переход
  25. на метку 026D7F8A). Попробуем проверить гипотезу о том, что все, что нужно
  26. для "освобождения" программы от проверки даты - это сделать прямо здесь
  27. достпуной кнопку "TRY". Попробуем сделать это прямо в отладчике: установим
  28. break на адрес 026D7F5F:
  29.  
  30. <p>&nbsp;<strong>bpx</strong> LoadLibraryA ; Ждем загрузки xsystem.dll
  31. <p>&nbsp; ...
  32. <p>&nbsp;<strong>bc 0</strong> ; clear breakpoint at LoadLibraryA
  33. <p>&nbsp;<strong>bpx</strong> 026D7F5F ; Set breakpoint
  34. <p>&nbsp; Ctrl+D...
  35.  
  36. <p>&nbsp; и попытаемся дождаться срабатывания break'а и "руками"
  37. поменять в отладчике значение регистра al сразу после lodsb: но вместо того,
  38. чтобы получить break на этом (026D7F5F) адресе, мы получаем GPF... Видно
  39. (в Sice'е), что машина пытается исполнять какой-то мусор в xsystem.dll.
  40.  
  41. <p>&nbsp; ...Так мы выяснили, что программа не только контролирует системную
  42. дату, но и сопротивляется нашим попыткам ее трейсить ;) Наверное, программа
  43. как-то динамически модифицирует свой код - например по адресу 026D7F5F -
  44. и break, который мы пытаемся установить, мы устанавливаем в то место, которое
  45. впоследствии будет изменено. Скажем, самораспаковывающийся или
  46. саморасшифровывающийся код может быть причиной тому. Попробуем как-то обойти
  47. это ограничение и вспоминаем про то, что программа неминуемо должна узнать
  48. системную дату - ведь ей необходимо ее сравнить с чем-то типа даты инсталляции.
  49. Win32-приложение обычно делает это при помощи API-функции
  50. GetSystemTime:
  51.  
  52. <p>&nbsp; GetSystemTime(offset структуры типа SYSTEMTIME)
  53. <p><code><pre>
  54. SYSTEMTIME STRUCT
  55.   wYear             WORD      ?
  56.   wMonth            WORD      ?
  57.   wDayOfWeek        WORD      ?
  58.   wDay              WORD      ?
  59.   wHour             WORD      ?
  60.   wMinute           WORD      ?
  61.   wSecond           WORD      ?
  62.   wMilliseconds     WORD      ?
  63. SYSTEMTIME ENDS
  64.  

  Устанавливаем break на эту API-функцию:

 bpx GetSystemTime

  И обнаруживаем, что вызов произошел из следующего места (опять xsystem.dll):

Код (Text):
  1.  
  2. 026D6DE3:
  3.   lea  esi,[ebp-100h]
  4.   push esi
  5.   <strong>call [ebp-4] ; call GetSystemTime </strong>
  6.   pop  ebx
  7. 026D6DEE:
  8.   movzx edx,word ptr [esi] ; d esi -> D3 07 03 00 05 00 1C
  9.   ; edx= 07D3h
  10.   shl  edx,8
  11.   mov  dl,[esi+02] ; Месяц
  12.   shl  edx,8
  13.   mov  dl,[esi+06] ; День
  14.   ; edx=07D3031C
  15.   push edx
  16. </code></pre>
  17.  
  18. <p>&nbsp; Уже видно, как программа работает с датой: код по адресу ~026D6DE3
  19. получает косвенным call'ом дату от Win, собирает в 32 битах edx год
  20. (07D3h=2003), месяц (03) и день (1Ch=28) - 28 марта 2003 года в данном случае,
  21. делает что-то еще и управление попадает к коду в ~026D7F5F, который уже
  22. знает, в валидном ли интервале дат мы находимся и закрывает кнопку "TRY", если
  23. это не так. Но мы помним, что нужно все же попробовать подменить результат
  24. манипуляций с датой прямо перед принятием решения о disable кнопки "TRY".
  25. Ранее нам сделать это не удалось - видимо, после загрузки dll в память ее
  26. (?-пока не знаем) код самомодифицируется и мешает ставить полноценные break'и.
  27. Однако есть надежда, что в момент выполнения кода по адресу ~026D6DE3 - кода,
  28. спрашивающего дату у Win - код в ~026D7F5F расшифрован и доступен для отладки.
  29. Повторяем попытку установить break на 026D7F5F, но уже после того, как мы
  30. попали в 026D6DE3 и опять получаем GPF ! Почему, ведь код уже явно виден
  31. в отладчике ? Может, дело в том, что адрес API EnableWindow прописывается
  32. динамически прямо в команду mov edi,const_addr ? Пробуем установить break
  33. на команду загрузки регистра esi адресом структуры (lea  esi,[ebp+2C63h])
  34. и ... оказываемся в нужном месте:
  35.  
  36. <p><code><pre>
  37. 026D7F5F:
  38.   mov  edi,User32!EnableWindow
  39.   ...
  40. 026D7F68:
  41.   <strong>lea  esi,[ebp+2C63h]</strong>
  42.   lodsb ; al = 90h если запрещенный период, иначе всегда 6Ah
  43.   cmp  al,90h
  44.   jnz  026D7F8A
  45.   push 03E8h
  46.   push dword ptr [edi+8]
  47.   call [ebp+2B50h]
  48.   push 0 ; FALSE - disable button "TRY"
  49.   push eax
  50.   call [ebp+2B54h] ; Точка вызова EnableWindow
  51. 026D7F8A:
  52.   popad
  53.   ...
  54. </code></pre>
  55.  
  56. <p>&nbsp; Ура !? Что ж, проверим: модифицируем (в отладчике) регистр al после
  57. лодсб и отпускаем программу...
  58.  
  59. <p>&nbsp;<IMG SRC="/archive/pub/23/pics/cool3d/en_btn.jpg" HEIGHT="350" WIDTH="500">
  60.  
  61. <p>&nbsp; ...Так, кнопка "TRY" доступна (хотя Remaining Days = 0). Жмем ее и
  62. видим всего лишь вот этот MessageBox:
  63.  
  64. <p>&nbsp;<IMG SRC="/archive/pub/23/pics/cool3d/alert.jpg" HEIGHT="150" WIDTH="200">
  65.  
  66. <p>&nbsp; Вот и познакомились ;) Оказывается, защиту зовут "xLok". А разрешить
  67. жать на кнопку - не значит получить доступ к функциональности ;(
  68.  
  69. <p>&nbsp; Но что если данными для проверки валидности даты для кода
  70. в ~026D7F5F является не байт, а слово или даже что-то большее ? На такую
  71. мысль может навести пара команд "lea esi,[ebp+2C63h], lodsb" вместо
  72. "cmp byte ptr [ebp+2C63h],90h", хотя это может быть "приколом" компиллятора.
  73. Внимательнее поизучаем байты в структуре, адресуемой по [ebp+2C63h] для двух
  74. дат: для даты в интервале работы и вне ее:
  75.  
  76. <p><code><pre>
  77. Работа возможна:  6A 01 FF 73 08 FF 95 54 ...
  78. Работа запрещена: 90 90 FF 73 08 E8 9D 00 ...
  79. </code></pre>
  80.  
  81. <p>&nbsp; Установим дату вне интервала работы, опять остановимся в
  82. отладчике в точке 026D7F68, поменяем "неверные" байты на "верные"
  83. (90 90 FF ... -> 6A 01 FF ... ) и "отпустим" программу. MessageBox'а от
  84. xLok'а больше нет, но есть банальный GPF...
  85.  
  86. <p>&nbsp; Следовательно, с большой вероятностью можно заключить, что защита
  87. выполняет (динамически) расшифровку какой-то части рабочего кода и снятие
  88. всех ее проверок после расшифровки приводит к неизбежному краху, так как
  89. этот код расшифровывается неверно вне интервала работы программы. Видимо,
  90. защита использует дату (точнее, не саму дату, а некоторую функцию от нее,
  91. которая постоянна на интервале работы программы) как ключ шифрования части
  92. своего кода. Проверим это: в этом случае будет достаточно подменить
  93. системную дату, которую защита спрашивает у Win. В отладчике это легко сделать:
  94. опять устанавливаем break на GetSystemTime, возвращаемся в xsystem.dll к
  95. коду в 026D6DEE и "патчим" значение структуры SYSTEMTIME (или значение edx
  96. чуть ниже) датой инсталляции... Все OK, программа отлично работает и ничего
  97. не запрещает.
  98.  
  99. <p>&nbsp; Здорово, вроде бы осталось поменять команды инициализации edx
  100. (код в 026D6DEE и ниже) на что-то вроде таких:
  101.  
  102. <p><code><pre>
  103. 026D6DEE:
  104. ; Было так:
  105. ; movzx edx,word ptr [esi] ; d esi -> D3 07 03 00 05 00 1C
  106. ; shl  edx,8
  107. ; mov  dl,[esi+02] ; Месяц
  108. ; shl  edx,8
  109. ;  mov  dl,[esi+06] ; День
  110. ; А будет так:
  111.   mov  edx,Const_Valid_Date
  112.   nop
  113.   ...
  114.   nop
  115.   push edx
  116. </code></pre>
  117.  
  118. <p>&nbsp; и все будет работать ? Пробуем найти опкоды оригинальных инструкций
  119. в DLL:
  120.  
  121. <p><code><pre>
  122.   Команды             Опкод
  123. movzx edx,w [esi]    0F B7 16
  124. shl   edx,8          C1 E2 08
  125. mov   dl,[esi+2]     8A 56 02
  126. ...
  127. </code></pre>
  128.  
  129. и не находим их ! Этого следовало ожидать еще тогда, когда стало ясно,
  130. что код самомодифицирующийся. Что делать дальше ? Может быть, имеет смысл
  131. попробовать найти расшифровшик, проанализировать его алгоритм и "пропатчить"
  132. нужные байты в соответствии с алгоритмом ? Пусть алгоритм дешифровщика
  133. состоит в наложении xor-последовательности на байты DLL:
  134.  
  135. <p><code><pre>
  136.   Команды             Опкод    Шифро-Опкод в DLL  (Опкод) xor (Шифро-Опкод в DLL)
  137. movzx edx,w [esi]    0F B7 16     B5 2A 1A              BA 9D 0C
  138. shl   edx,8          C1 E2 08     C4 45 2E              05 A7 26
  139. ...
  140. </code></pre>
  141.  
  142. <p>&nbsp; В этом случае не составило бы труда подготовить те "новые" опкоды,
  143. которые мы собираемся поместить на место старых - нужно выполнить над
  144. ними операцию с той же xor-последовательностью - как это делает пока
  145. неизвестный нам расшифровщик:
  146.  
  147. <p><code><pre>
  148.   Команды             Опкод_новый   (Опкод) xor (Шифро-Опкод в DLL) Результат
  149. mov edx,07D3031Ch    BA 1C 03 D3 07       BA 9D 0C 05 A7             00 81 0F...
  150. nop  
  151. ...
  152. </code></pre>
  153.  
  154. <p>&nbsp; Помещаем в файл xsystem.DLL по смещению (в файле) 6DEE подготовленные
  155. таким образом байты и пытаемся смотреть, что же сделал с ними расшифровщик
  156. - а в результате видим такой вот MessageBox:
  157.  
  158. <p>&nbsp;<IMG SRC="/archive/pub/23/pics/cool3d/debug_m.jpg" HEIGHT="150" WIDTH="200">
  159.  
  160. <p>&nbsp; Да, защите удалось не только скрыть результат работы расшифровщика
  161. (если он вообще отработал) но и определить модификацию собственного кода.
  162. Неплохо ! Как же ему это удалось ? Попробуем "отловить" его на обращении к
  163. памяти, содержащей измененные коды:
  164.  
  165. <p>&nbsp;<strong>bpm</strong> 026D6DEE
  166.  
  167. <p>&nbsp; и, действительно, наблюдаем следующий код:
  168.  
  169. <p><code><pre>
  170. 026D510C:
  171.   push esi
  172.   push ecx
  173.   push eax
  174.   ...
  175. 026D5115:
  176.   mov  ecx,49A0h
  177.   xor  edx,edx
  178.   xor  eax,eax
  179. @@GetCRC:
  180.   <strong>lodsb ; esi=026D6DEFh </strong>
  181.   add  edx,eax
  182.   dec  ecx
  183.   jnz  @@GetCRC
  184.   ...
  185.   xchg edx,eax ; eax->1FBFFAh
  186.   pop  edx
  187.   pop  ecx
  188.   pop  esi
  189.   add  dword ptr [esp],5
  190. 026В515A:
  191.   ret
  192. </code></pre>
  193.  
  194. <p>&nbsp; Очень интересный код ! Похоже на подсчет CRC (возвращает в eax),
  195. причем регион подсчета CRC включает и критичные для нас адреса в ~026D6DEE.
  196. Следовательно, прежде чем воевать с их расшифровщиком, нам нужно победить
  197. подсчет контрольной суммы. Открыты ли байты подпрограммы подсчета CRC в DLL ?
  198.  
  199. <p><code><pre>
  200. 026D510C:
  201.   Команда / Опкод
  202.   push esi / 56h
  203.   push ecx / 51h
  204.   push eax / 52h
  205.   ...
  206. </code></pre>
  207.  
  208. <p>&nbsp; Ищем их в файле DLL по смещению 510Ch и находим ! Следовательно, мы
  209. можем без особой боязни заменить оригинальную подпрограмму подсчета CRC на
  210. что-то вроде:
  211.  
  212. <p><code><pre>
  213. @@NewCRCProc:
  214.   mov  eax,1FBFFAh
  215.   add  dword ptr [esp],5
  216.   ret
  217. @@EndOfNewCRC:
  218.   db (026D515Bh-026D510Ch) - (@@EndOfNewCRC-@@NewCRCProc) dup(90h)
  219. @@NewCRCProcDone:
  220. </code></pre>
  221.  
  222. <p>&nbsp; При этом мы вернем нужное где-то дальше CRC (в eax) без всякого
  223. подсчета - что зря процессор гонять, хитро вернемся назад
  224. (add  dword ptr [esp],5) и забьем все остальное место nop'ами. Патчим таким
  225. образом DLL и получаем ... GPF ! Оба-на, защита перестала детектировать
  226. сосбтвенную модификацию но и программа "рухнула" ;( Неужели мы неправильно
  227. составили новый код подсчета CRC ?... Стоп, а ведь мы еще раньше поменяли
  228. байты, относящиеся к получению даты (в ~026D6DEE) ! Установим break на
  229. API GetSystemTime, вернемся к коду получения даты в DLL и видим,
  230. что расшифровщик явно сделал не то, что мы ожидали:
  231.  
  232. <p><code><pre>
  233.   Команды_новые   Результат работы расшифровщика / Опкод
  234. 026D6DEE:
  235. mov edx,07D3031Ch   sbb  byte ptr [esi] / 1D 82 1E 1D
  236. nop                 mov  bl,7Fh / B3 7F
  237. ...
  238. </code></pre>
  239.  
  240. <p>&nbsp; Следовательно, алгоритм расшифровщика производит расшифровку байт
  241. в ~026D6DEE в зависимости от исходных байт. Т.е. это не простое наложение
  242. XOR-последовательности; расшифровка каждого байта зависит от того, как
  243. был расшифрованы все предидущие. Алгоритм расшифровщика нам по-прежнему
  244. неизвестен и мы не можем даже пытаться осуществлять атаку на его шифр.
  245. Откатим пока эти изменения, оставив лишь получение CRC и продолжим следить в
  246. отладчике за ходом выполнения программы защиты после возврата из подпрограммы
  247. подсчета CRC:
  248.  
  249. <p><code><pre>
  250. 026D5366:
  251.   sub  eax,1FBfFAh ; если пришло верное CRC=1FBfFAh, то в eax будет 0
  252.   pop  ebx
  253.   jz   026D5382
  254.   ...
  255. 026D5382:
  256.   mov  [ebp+3C16h],ebx
  257.   mov  edx,[ebp+3C36h]
  258.   push dword ptr fs:[0]
  259.   mov  fs:[0],esp
  260.   push eax
  261.   push ebp
  262.   jmp  026D53A1
  263.   ...
  264. 026D53A1:
  265.   pushfd
  266.   invalid (0F1h)
  267.   db 0BEh,0F0h,...
  268. </code></pre>
  269.  
  270. <p>&nbsp; Если продолжить в отладчике пошагово трассировать код с 026D53A2,
  271. то налетаешь на GPF. Но сразу бросаются в глаза явно вычурные для win32-кода
  272. команды работы с сегментными регистрами (mov  fs:[0],esp). Если уж приложение
  273. взялось за это, то явно не с добрыми намерениями ;) На самом деле таким
  274. образом в windows пользовательским программам дозволяется установить свой
  275. обработчик исключительных ситуаций и в случае таковых получить управление
  276. и попытаться их обработать (интересно, кому-нибудь удавалось обрабатывать их
  277. инчае как выдать сообщение "Программа выполнила ... и будет закрыта" ?...).
  278. Далее мы увидим, что иногда такой обработчик может выполнять некоторые
  279. "полезные" действия.
  280.  
  281. <p>&nbsp; В двух словах о таких обработчиках. Эта штука называется "SEH"
  282. - structure exeption handler. Для того, чтобы обработчик правильно получил
  283. управление, необходимо:
  284.  
  285. <p><code><pre>
  286.  - поместить в стек смещение обработчика;
  287.  - сохранить смещение старого обработчика на том же стеке;
  288.  - записать по селектору из FS, смещению 0 содержимое esp
  289. </code></pre>
  290.  
  291. <p>&nbsp; Сам обработчик вызвается в C-формате, при этом получая кучу параметров
  292. - указатель на структуру регистров в момент исключения и т.п.:
  293.  
  294. <p><code><pre>
  295. SEHproc proc C pExcept: dword, pFrame: dword, pContext: dword, pDispatch: dword
  296.     PrintException pExcept
  297.     ...
  298.     ret
  299. SEHproc endp
  300. </code></pre>
  301.  
  302. <p>&nbsp; Но самое главное - это то, что он может вернуться в достаточно
  303. произвольное место кода (включая и вызвавшее исключение), при этом
  304. уведомив диспечер о том, что исключение им обработано
  305. (например, mov eax, ExceptionContinueExecution перед выходом) и то,
  306. что он имеет право записи в сегмент команд (это необходимо для исправления
  307. "дефектного" кода). Таким образом, обработчик исключения защиты явно собирается
  308. именно этим заниматься: подать управление на неверный опкод, поймать expeption,
  309. дешифровать некоторые байты, вернуться опять на неверный опкод (не обязательно
  310. первый), и так до тех пор, пока необходимый код не будет полностью расшифрован.
  311.  
  312. <p>&nbsp; Где же защита разместила свой обработчик ? Нет нужды копаться в
  313. командах, предшествующих коду установки SEH (~026D5382), можно просто
  314. посмотреть стек в этот момент (скажем, после выполнения mov fs:[0],esp):
  315.  
  316. <p>&nbsp;<strong>d</strong> esp
  317.  
  318. <p>&nbsp; Оказыватся, адрес у обработчика равен 026D5040, а вот и он сам:
  319.  
  320. <p><code><pre>
  321. 026D5040:
  322.   pushad
  323.   mov  edx,0C8EC918Bh
  324.   call 026D504B
  325. 026D504B:
  326.   pop  edi ; edi-> 026D504B, получение текущего смещения
  327.   mov  esi,36h
  328.   add  esi,edi ; esi=026D5081h
  329.   mov  ecx,94h
  330.   push esi
  331.   sub  esi,9
  332.   mov  edi,esi
  333.   xor  eax,eax
  334. 026D5061:
  335.   lodsb
  336.   xor  eax,edx
  337.   rol  edx,5
  338.   imul edx,edx,0FB712715h
  339.   add  eax,0AB358CDFh
  340.   stosb
  341.   dec  ecx
  342.   jnz  026D5061
  343. 026D5078:
  344.   db 0F4h,041h,0ECh,076h,03Ah...
  345. 026D5081:
  346.   ...
  347. </code></pre>
  348.  
  349. <p>&nbsp; Итак, обработчик SEH сразу после получения управления начинает
  350. что-то расшифровывать с адреса 026D5078 и ниже довольно незамысловатым
  351. алгоритмом, немного странно реализованным (вместо add  eax,0AB358CDFh
  352. достаточно add al,0DFh). Размер байт для дешифровки невелик (mov ecx,94h).
  353. Дожидаемся возможности установить break в 026D5078 - после первого же stosb
  354. там появлется pop esi и после срабатывания break'а видим весь расшифрованный
  355. код:
  356.  
  357. <p><code><pre>
  358. 026D5078:
  359.   pop  esi
  360.   mov  eax,[esp+28h]
  361.   mov  [eax+4],esi ; <-026D5081h
  362.   popad
  363. 026D5081:
  364.   push ebp
  365.   mov  ebp,esp
  366.   push esi,edi,ebx,ecx,edx
  367.   mov  eax,[ebp+10h]
  368.   mov  edi,[eax+0B8h]
  369. 026D5093:
  370.   mov  edx,2
  371. 026D5097:
  372.   jmp  026D5099
  373. 026D5099:
  374.   call 026D509E
  375. 026D509E:
  376.   pop  ebx ; ebx<-текущее смещение 026D509E
  377.   add  ebx,0FFFFFFE3h ; ebx=026D5081h
  378.   add  edx,[eax+0B4h]
  379.   mov  [ebx+12h],edx
  380.   ...
  381.   mov  ebx,0BB51B5E3h
  382.   test esi,esi
  383.   jz   026D50CE
  384.   xor  [esi],bl
  385. 026D50CE:
  386.   cmp  byte ptr [edi],09Dh
  387.   jz   026D50DF
  388.   xor  [edi],bl
  389.   or   dword ptr [eax+0C0h],100h
  390. 026D50DF:
  391.   xor  ebx,ebx
  392.   mov  [eax+4],ebx, mov  [eax+8],ebx, mov  [eax+10h],ebx
  393.   mov  dword ptr [eax+18h],101h
  394.   mov  [edx],edi
  395.   xor  eax,eax
  396.   push eax, push eax, dec eax, push eax, mov eax,ofs Kernel!FlushInstructionCashe
  397.   call eax
  398.   xor  eax,eax
  399.   pop  edx,ecx,ebx,edi,esi,ebp
  400.   ret  ; Последняя расшифрованная команда
  401. 026D510Ch:
  402. </code></pre>
  403.  
  404. <p>&nbsp; Что же любопытного в раскрывшемся коде ? Прежде всего обратим
  405. внимание на характерный код в 026D5081 - push ebp, mov ebp,esp. Это явно
  406. оформлено начало какой-то процедуры, и она заканчивается в 026D510B. Командами
  407.  
  408. <p><code><pre>
  409.   mov  eax,[esp+28h]
  410.   mov  [eax+4],esi ; <-026D5081h
  411. </code></pre>
  412.  
  413. <p>&nbsp; декриптор определил адрес <strong>нового</strong> обработчика SEH
  414. - теперь это будет код в 026D5081. При следующих исключениях всегда будет
  415. вызываться именно эта процедура, что-то расшифровывающая командами:
  416.  
  417. <p><code><pre>
  418.   xor  [esi],bl
  419. 026D50CE:
  420.   cmp  byte ptr [edi],09Dh
  421.   jz   026D50DF
  422.   xor  [edi],bl
  423.   or   dword ptr [eax+0C0h],100h
  424. 026D50DF:
  425. </code></pre>
  426.  
  427. <p>&nbsp; Но в данный момент код получения даты (в ~026D6DEE) еще не расшифрован.
  428. Может быть, только что расшифрованная SEH-подпрограмма 026D5081 и есть ее
  429. декриптор ? Устанавливаем break на начало 026D5081:
  430.  
  431. <p>&nbsp;<strong>bpx</strong> 026D5081
  432.  
  433. <p>&nbsp; и наблюдаем, как раз за разом управление переходит к SEH-обработчику.
  434. Он явно расшифровывает какой-то код и хотелось бы остановить программу в
  435. тот момент, когда он полностью закончит свою работу. Довольно утомительно было
  436. бы жать Ctrl-D столько раз, поэтому я написал небольшой код, заменяющий
  437. собой распаковщик SEH-обработчика:
  438.  
  439. <p><code><pre>
  440. ; Новые команды по адресу 026D5040 (первоначальный SEH-обработчик)
  441. @@NewBytes:
  442.   pushad
  443.   call @@GetOfsNewBytes
  444. @@GetOfsNewBytes:
  445.   pop  esi
  446.   add  esi,offset @@ArtBytes - @@GetOfsNewBytes
  447.   mov  eax,[esp+28h]
  448.   mov  [eax+4],esi
  449.   popad
  450.   db   0EBh ; jmp на адрес 026D5081
  451.   db   (@@NewBytes+40h) - $
  452. @@ArtBytes:
  453.   pushad
  454.   call @@GetOfs
  455. @@GetOfs:
  456.   pop  eax
  457.   inc  word ptr [eax+(offset CounterCalls - offset @@GetOfs)]
  458.   mov  ax,word ptr [eax+(offset CounterCalls - offset @@GetOfs)]
  459.   popad
  460.   db   0EBh ; jmp на адрес 026D5081
  461.   db   (@@NewBytes+40h) - $
  462. CounterCalls dw   0
  463. @@DoneNewBytes:
  464. LastNops db (@@DoneOldBytes - @@OldBytes) dup (90h) ; nop only
  465. </code></pre>
  466.  
  467. <p>&nbsp; Новый код в отличии от старого не декриптует SEH-обработчик (это
  468. можно сделать прямо в файле xsystem.dll и не обременять код графического
  469. редактора дешифрацией), а устанавливает новый адрес SEH-обработчика - это
  470. будет подпрогрммка @@ArtBytes, которая только лишь вычисляет число call'ов
  471. SEH'а и передает jmp'ом управление на старый адрес 026D5081. Теперь в Sice'е
  472. можно ставить <stong>условный</stong> break на команде
  473. mov  ax,word ptr [eax+(offset CounterCalls - offset @@GetOfs)]:
  474.  
  475. <p>&nbsp;<strong>bpx</strong> <addr> IF (AX==0x100)
  476.  
  477. <p>&nbsp; Заранее, конечно, число call'ов SEH'а неизвестно, но простым
  478. методом деления "отрезка" пополам можно довольно быстро добраться до конца
  479. работы SEH-обработчика. Трейсим последний вызов по SEH-обработчика и видим,
  480. что:
  481.  
  482. <p>&nbsp; - Даже когда он закончил свою работу, код получения даты (в ~026D6DEE)
  483. нерасшифрован;
  484. <p>&nbsp; - Последний свой xor SEH-обработчик выполнил с адресом 026D540Ch:
  485.  
  486. <p><code><pre>
  487.   xor  [esi],bl ; edi=026D540Ch, там 75h, bl=3Eh
  488. 026D50CE:
  489.   cmp  byte ptr [edi],09Dh
  490.   ...
  491. </code></pre>
  492.  
  493. <p>&nbsp; Таким образом, SEH-обработчик-дешифровщик не расшифровал код
  494. получения даты. Но что за байты появились по адресу 026D540Ch ?...
  495.  
  496. <p><code><pre>
  497. 026D540C:
  498.   popfd
  499.   call 026D5412 ; get current offset
  500. 026D5412:
  501.   pop  esi
  502.   add  esi,02Eh
  503.   mov  edi,esi
  504.   mov  ecx,0CF9h
  505.   mov  edx,9548E9F7h
  506. 026D5425:
  507.   lodsd
  508.   xor  eax,edx
  509.   add  eax,54ADF121h
  510.   xor  eax,ecx
  511.   rol  edx,5
  512.   imul edx,edx,70C967BEh
  513.   xor  edx,ecx
  514.   stosd
  515.   dec  ecx
  516.   jnz  026D5425
  517.   jmp  026D5450
  518. 026D5440:
  519.   ...
  520. </code></pre>
  521.  
  522. <p>&nbsp; О, старые знакомые ! <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3    :smile3:"> Опять переносимый (call 026D5412, pop esi)
  523. код дешифрует нижележащие байты. Ставим break в конец цикла, дожидаемся
  524. его окончания и с нетерпением смотрим ... нет, не байты после 026D5440, а
  525. байты получения даты - ~026D6DEE. Они расшифрованы, теперь - ура - мы знаем
  526. кто их декриптует и этот кто-то (026D540C - последний дешифровщик) крайне
  527. мал, чтобы возится с его расшифровщиком (обработчик SEH'а) - проще вбить
  528. его, уже нам известный на то же место, где сейчас лежит его зашифрованный
  529. собрат. Только тогда нужно будет дезактивировать его расшифровщик
  530. (обработчик SEH'а) - а то он все испортит ;) Как же это сделать ? Вернемся
  531. к моменту инсталляции SEH'а:
  532.  
  533. <p><code><pre>
  534.   ...
  535. 026D5382:
  536.   mov  [ebp+3C16h],ebx
  537.   mov  edx,[ebp+3C36h]
  538.   push dword ptr fs:[0]
  539.   mov  fs:[0],esp
  540.   push eax
  541.   push ebp
  542.   jmp  026D53A1
  543.   ...
  544. 026D53A1:
  545.   pushfd
  546.   invalid (0F1h)
  547.   db 0BEh,0F0h,...
  548. </code></pre>
  549.  
  550. <p>&nbsp; Что будет, если вместо invalid-команды, намеренно сделанной для
  551. генерации исключения, сделать переход на дешифровщик в 026D540C, который
  552. теперь, как мы планируем, будет записан нами в открытом виде прямо в DLL ?
  553. SEH-обработчик явно не вызовется, осталось опасность потерять стек при таком
  554. прыжке. Но парная команда к pushfd - последняя команда перед вызовом
  555. SEH-обработчика через исключение - popfd:
  556.  
  557. <p><code><pre>
  558. 026D540C:
  559.   popfd
  560.   call 026D5412 ; get current offset
  561.   ...
  562. </code></pre>
  563.  
  564. <p>&nbsp; как будто бы явно написана разработчиками защиты с целью восстановить
  565. состояние процесса после отработки SEH'а. Так и поступим. Остался последний
  566. момент: где разместить код, который поменяет пресловутые команды получения
  567. даты на одну спокойную mov edx,Const_Valid_Date и парочку nop'ов ?
  568.  
  569. <p>&nbsp; Ниже привожу свое решение, возможно немного надуманное. Поскольку этот
  570. код хотелось бы разместить после отработки расшифровщика 026D540C, т.е. тогда,
  571. когда уже код от 026D5440 и далее расшифрован, то он должен быть после
  572. последней команды расшифровывающего цикла:
  573.  
  574. <p><code><pre>
  575. 026D540C:
  576.   ...
  577.   dec  ecx
  578.   jnz  026D5425
  579.   ; Здесь должен быть код, вставляющий команду mov edx,Const_Valid_Date
  580.   jmp  026D5450
  581. 026D5440:
  582.   ...
  583. </code></pre>
  584.  
  585. <p>&nbsp; Вместо двух байт короткого jmp'а сложновато сделать такое, но если
  586. немного пооптимизировать код расшифровщика и вспомнить, что после удаления
  587. старого кода подсчета CRC осталась куча места, то можно написать следующее:
  588.  
  589. <p>&nbsp; Этой командой мы закроем выполнение SEH'а:
  590.  
  591. <p><code><pre>
  592. ; Этот jump мы поместим на место всяких invalid'ов, чтобы SEH не вызывался
  593. ; Сам SEH-обработчик оставим в покое
  594. @@NewJumpAt53A2:
  595.   jmp  $+(540Ch-53A2h)
  596. @@NewJumpAt53A2Done:
  597. </code></pre>
  598.  
  599. <p>&nbsp; Это код нового, открытого декриптора по адресу 026D540C:
  600.  
  601. <p><code><pre>
  602. @@Decryptor2:
  603.   popfd
  604.   call @@GetOfsDecryptor2
  605. @@GetOfsDecryptor2:
  606.   pop  esi
  607.   ;; db   081h,0C6h,02Eh,000h,000h,000h ; add  esi,2Eh - в оригинале
  608.   ;; был именно вариант из 6-ти байт
  609.   add  esi,2Eh
  610.   mov  edi,esi
  611.   mov  ecx,0CF9h
  612.   mov  edx,9548E9F7h
  613. @@DecryptLast:
  614.   lodsd
  615.   xor  eax,edx
  616.   add  eax,54ADF121h
  617.   xor  eax,ecx
  618.   rol  edx,5
  619.   imul edx,edx,70C967BEh
  620.   xor  edx,ecx
  621.   stosd
  622. ;;  dec  ecx
  623. ;;  jnz  @@DecryptLast
  624.   loop @@DecryptLast
  625.   ; Переходим на кусок кода, который разместили внутри подпрограммы
  626.   ; подсчета CRC
  627.   mov  si,510Ch+(offset @@PatchAt026D6DEE-offset @@NewCRCProc)
  628.   jmp  esi
  629. @@Decryptor2Done:
  630. </code></pre>
  631.  
  632. <p>&nbsp; Это новый код подсчета CRC вместе с подпрограммой модификации
  633. кода получения даты в 026D6DEE:
  634.  
  635. <p><code><pre>
  636. ;
  637. ; New subprogram 026D510Ch - calculate CRC + patch code at 026D6DEE
  638. ;
  639. @@NewCRCProc:
  640.   mov  eax,1FBFFAh
  641.   add  dword ptr [esp],5
  642.   ret
  643. @@PatchAt026D6DEE:
  644. ;
  645. ; Необходимо:
  646. ; 1. Пропатчить байты в ~026D6DEE - подменить дату
  647. ; 2. Пропатчить байты в ~026D6E0B - подменить дату для вычисления Remaining Days
  648. ; 3. Вернуться в 026D5450
  649. ;
  650. ; Сделать mov  edx,07D3031Ch ; Year + Month + Day -> 0BAh,01Ch,003h,0D3h,007h
  651.   mov  si,6DF7h
  652.   mov  byte ptr [esi],0BAh ; mov edx,...
  653. @@PatchCommand:
  654.   mov  dword ptr [esi+1],07D3031Ch ; Year + Month + Day
  655.   mov  byte ptr [esi+5],90h ; nop
  656. ; Пропатчить получение стратовой даты для вычисления Remaining Days -
  657. ; - сделать mov  edx,07D3031Ch вместо xor eax,7895ADEBh по адресу 6E0Bh
  658.   mov  si,6E0Bh
  659.   mov  byte ptr [esi],0B8h ; mov eax,...
  660. @@PatchCommandRemainingDays:
  661.   mov  dword ptr [esi+1],07D3031Ch ; Year + Month + Day
  662. ; Возвращаемся
  663.   mov  si,5450h
  664.   jmp  esi
  665. </code></pre>
  666.  
  667. <p>&nbsp; Вот вроде бы и все ;) Хотя далее есть еще несколько пугающих
  668. кусков типа:
  669.  
  670. <p><code><pre>
  671. 026D6C52:
  672.   lea  esi,[ebx-100h]
  673.   ...
  674.   call 026D7623
  675.   <strong>int  62h ; !!!</strong>
  676.   add  eax,0FCfC4C4h
  677.   ...
  678. </code></pre>
  679.  
  680. <p>&nbsp; которые заставили меня лишний раз заглянуть в Ральфа Брауна, но
  681. особой опасности они не представляют ;)
  682.  
  683. <p align="left"><b>Немного извращений или граффити</b>
  684.  
  685. <p>&nbsp; После реализации подмены системной даты и многократных смен системной
  686. даты в целях выяснинения особенностей защиты программы можно наблюдать
  687. интересный эффект:
  688.  
  689. <p>&nbsp;<IMG SRC="http://www.wasm.ru/wault/pub/23/pics/cool3d/Rem_Days.jpg" HEIGHT="350" WIDTH="500">
  690.  
  691. <p>&nbsp; xsystem.dll уже изменена, все работает, но вот с числом оставшихся
  692. дней что-то не то. Как бы не мешает, но некрасиво... Любопытен тот факт,
  693. что обратная замена исходной DLL дела не меняет: число оставшихся дней
  694. меняется, но как-то "криво". Есть и такой "баг": если даже взять нетронутую
  695. версию программы, перевести дату на некоторое число дней вперед, запустить
  696. программу, а следом вернуть дату на место, то при первом запуске программа
  697. не позволяет работать (число оставшихся дней равно 0, кнопка "TRY" недоступна).
  698.  
  699. <p>&nbsp; Анализ кода xsystem.dll показал, что после того, как системная
  700. дата получена программой, выполняется следующий код:
  701.  
  702. <p><code><pre>
  703. 026D6E03:
  704.   mov  ecx,eax
  705.   <strong>mov  eax,[ebp+3202h]</strong>
  706.   xor  eax,7895ADEBh
  707.   test eax,eax
  708.   ...
  709. </code></pre>
  710.  
  711. <p>&nbsp; - из некоторой области памяти достатется число, которое
  712. "расшифровывается" xor-ом и далее (код здесь не приводится) трактуется
  713. как дата начала работы программы (инсталляции). Поэтому для завершенности
  714. изменений в программе можно поменять команду "xor eax,7895ADEBh"
  715. на "mov eax,Const_Valid_Date". В этом случае программа будет выдавать
  716. 30 дней до конца trial'а. Но можно немного поизвращаться и оставить
  717. число Remaining Days вот таким:
  718.  
  719. <p>&nbsp;<IMG SRC="/archive/pub/23/pics/cool3d/777_date.jpg" HEIGHT="350" WIDTH="500">
  720.  
  721. <p>&nbsp;
  722.  
  723. <p align="left"><b>Исходный текст патчера</b>
  724.  
  725. <p>&nbsp; Ниже приводится текст почти полный текст программы, выполняющей
  726. описанные выше действия. Некоторые вспомогательные подпрограммы типа
  727. GetCL пропущены.
  728.  
  729. <p><code><pre>
  730. ; ***
  731. ; Патч проги Ulead COOL 3D 3.5 ;)
  732. ; ***
  733. .386
  734. .model flat,stdcall
  735. option casemap :none   ; case sensitive
  736. include \masm32\include\windows.inc
  737. include \masm32\include\kernel32.inc
  738. includelib \masm32\lib\kernel32.lib
  739.  
  740. ; ------------------------------------
  741. ; Read text at end of module for usage
  742.   GetCL PROTO :DWORD,:DWORD
  743. ; ------------------------------------
  744. .data?
  745. bRead         dd ? ; Байт прочитано
  746. bWrite        dd ? ; Байт записано
  747. hConsole dd ?
  748. .code
  749. start:
  750.   cld
  751. ; Получаем хэндл консоли
  752.   invoke GetStdHandle,STD_OUTPUT_HANDLE
  753.   mov  dword ptr hConsole,eax
  754. ; ***
  755. ; Открываем лог
  756. ; ***
  757. .data
  758. LogFileName   db "patch_xsystem01.log",0
  759. .data?
  760. LogDescr      dd ?
  761. .code
  762. ; Открыть лог, если есть он уже
  763.   invoke CreateFile,offset LogFileName,GENERIC_WRITE,FILE_SHARE_WRITE+FILE_SHARE_READ,\
  764.       0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
  765.   mov  LogDescr,eax          ; Save file descriptor
  766.   inc  eax
  767.   jnz  @@LogFileOpen
  768. ; Видимо, нет. Тогда попробовать создать  
  769.   invoke CreateFile,offset LogFileName,GENERIC_WRITE,FILE_SHARE_WRITE+FILE_SHARE_READ,\
  770.       0,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0
  771.   mov  LogDescr,eax          ; Save file descriptor
  772.   inc  eax
  773.   jnz  @@LogFileOpen  
  774. .data
  775. OpenErrorMsg db "Log file not created !",0Ah,0
  776. .code
  777.   push offset OpenErrorMsg
  778.   call Write_Log
  779.   jmp  Exit
  780. @@LogFileOpen:
  781. ; Встать в конец лога
  782.   invoke SetFilePointer,LogDescr,0,NULL,FILE_END
  783.  
  784.   jmp  @@SkipBytes
  785.  
  786. ;
  787. ; New subprogram 026D510Ch - calculate CRC + patch code at 026D6DEE
  788. ;
  789.  
  790. @@NewCRCProc:
  791.   mov  eax,1FBFFAh
  792.   add  dword ptr [esp],5
  793.   ret
  794. @@PatchAt026D6DEE:
  795. ;
  796. ; Необходимо:
  797. ; 1. Пропатчить байты в ~026D6DEE - подменить дату
  798. ; 2. Вернуться в 026D5450
  799. ;
  800. ; Сделать mov  edx,07D3031Ch ; Year + Month + Day -> 0BAh,01Ch,003h,0D3h,007h
  801.   mov  si,6DF7h
  802.   mov  byte ptr [esi],0BAh ; mov edx,...
  803. @@PatchCommand:
  804.   mov  dword ptr [esi+1],07D3031Ch ; Year + Month + Day
  805.   mov  byte ptr [esi+5],90h ; nop
  806. ; Пропатчить получение стратовой даты для вычисления Remaining Days -
  807. ; - сделать mov  edx,07D3031Ch вместо xor eax,7895ADEBh по адресу 6E0Bh
  808.   mov  si,6E0Bh
  809.   mov  byte ptr [esi],0B8h ; mov eax,...
  810. @@PatchCommandRemainingDays:
  811.   mov  dword ptr [esi+1],07D3031Ch ; Year + Month + Day
  812. ; Возвращаемся
  813.   mov  si,5450h
  814.   jmp  esi
  815.  
  816. @@EndOfNewCRC:
  817.   db (026D515Bh-026D510Ch) - (@@EndOfNewCRC-@@NewCRCProc) dup(90h)
  818. @@NewCRCProcDone:
  819.  
  820. @@Decryptor2:
  821.   popfd
  822.   call @@GetOfsDecryptor2
  823. @@GetOfsDecryptor2:
  824.   pop  esi
  825.   ;; db   081h,0C6h,02Eh,000h,000h,000h ; add  esi,2Eh
  826.   add  esi,2Eh
  827.   mov  edi,esi
  828.   mov  ecx,0CF9h
  829.   mov  edx,9548E9F7h
  830. @@DecryptLast:
  831.   lodsd
  832.   xor  eax,edx
  833.   add  eax,54ADF121h
  834.   xor  eax,ecx
  835.   rol  edx,5
  836.   imul edx,edx,70C967BEh
  837.   xor  edx,ecx
  838.   stosd
  839. ;;  dec  ecx
  840. ;;  jnz  @@DecryptLast
  841.   loop @@DecryptLast
  842. ;;  jmp  $+(5450h-543Eh)
  843.   mov  si,510Ch+(offset @@PatchAt026D6DEE-offset @@NewCRCProc)
  844.   jmp  esi
  845. @@Decryptor2Done:
  846.  
  847. @@NewJumpAt53A2:
  848.   jmp  $+(540Ch-53A2h)
  849. @@NewJumpAt53A2Done:
  850.  
  851. @@SkipBytes:
  852.  
  853. ;
  854. ; Читаем дату, которую надо будет записать в файл
  855. ;
  856.  
  857. .data
  858. Year  dw 2003
  859. Month db 03
  860. Day   db 28
  861. .data?
  862. PatchDate db 128 dup(?)
  863. .code
  864.   invoke GetCL,1,offset PatchDate
  865.   cmp  eax,1
  866.   jz   @@DatePresent
  867. .data
  868. NoDateMsg db 0Ah,"No specifed date in command line ! Patch with 2003/03/28",0Ah,0
  869. .code
  870. @@NoDateSpec:
  871.   push offset NoDateMsg
  872.   call Write_Log
  873.   jmp  @@DateDone
  874. @@DatePresent:
  875.   mov  esi,offset PatchDate
  876.   call Str_Len
  877.   test ecx,ecx
  878.   jz   @@NoDateSpec
  879.   cld
  880. ; Year
  881.   lodsd
  882.   mov  ebx,eax
  883.   mov  ecx,4
  884.   xor  ebp,ebp
  885. @@GetYear:
  886.   imul ebp,ebp,10
  887.   movzx eax,bl
  888.   shr  ebx,8
  889.   sub  al,'0'
  890.   add  ebp,eax
  891.   loop @@GetYear
  892.   mov  eax,ebp
  893.   mov  Year,ax
  894.   lodsb
  895. ; Month
  896.   xor  eax,eax
  897.   xor  ebx,ebx
  898.   lodsw
  899.   sub  ax,'00'
  900.   mov  bl,ah
  901.   mov  ah,10
  902.   mul  ah
  903.   add  bl,al
  904.   mov  Month,bl
  905.   lodsb
  906. ; Day
  907.   xor  eax,eax
  908.   xor  ebx,ebx
  909.   lodsw
  910.   sub  ax,'00'
  911.   mov  bl,ah
  912.   mov  ah,10
  913.   mul  ah
  914.   add  bl,al
  915.   mov  Day,bl
  916. ; Display patched date
  917. .data
  918. PatchDateHead db 0Ah,"Patch date: "
  919. PatchDateStr  db "????/??/??",0Ah,0h
  920. .code
  921.   movzx eax,word ptr Year
  922.   mov  ebx,1000
  923.   mov  edi,offset PatchDateStr
  924.   call DecChar
  925.   movzx eax,byte ptr Month
  926.   mov  ebx,10
  927.   mov  edi,offset PatchDateStr+5
  928.   call DecChar
  929.   movzx eax,byte ptr Day
  930.   mov  ebx,10
  931.   mov  edi,offset PatchDateStr+8
  932.   call DecChar
  933.   push offset PatchDateHead
  934.   call Write_Log
  935.  
  936. @@DateDone:
  937.  
  938. ;
  939. ; Открываем DLL
  940. ;
  941. .data
  942. IniFileName db "xsystem.dll",0
  943. .data?
  944. IniDescr  dd ?
  945. .code
  946.   invoke CreateFile,offset IniFileName,GENERIC_WRITE or GENERIC_READ,\
  947.     FILE_SHARE_READ,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0
  948.   mov  IniDescr,eax          ; Save file descriptor
  949.   cmp  eax,0ffffffffh
  950.   jnz  @@DestFileOpen
  951. .data
  952. DestFileNotOpenMsg db "xsystem.dll not open (not exist ?)! Process aborted !",0Ah,0
  953. .code
  954.   push offset DestFileNotOpenMsg
  955.   call Write_Log
  956.   jmp  Exit
  957. @@DestFileOpen:
  958.  
  959. ;
  960. ; Патчим DLL
  961. ;
  962.  
  963. ;
  964. ; Новая процедура без CRC
  965. ;
  966.  
  967. ; Встать в нужное место
  968.   invoke SetFilePointer,IniDescr,0510Ch,NULL,FILE_BEGIN
  969. ; Копируем новый код в сегмент данных для установки даты
  970. .data?
  971. Data_@@NewCRCProc db (@@NewCRCProcDone-@@NewCRCProc) dup(?)
  972. .code
  973.   mov  ecx,@@NewCRCProcDone-@@NewCRCProc
  974.   mov  esi,offset @@NewCRCProc
  975.   mov  edi,offset Data_@@NewCRCProc
  976.   lea  ebx,[edi+(@@PatchCommand-@@NewCRCProc)+3]
  977.   lea  edx,[edi+(@@PatchCommandRemainingDays-@@NewCRCProc)+3]
  978.   rep  movsb
  979.   mov  ax,Year
  980.   shl  eax,16
  981.   mov  ah,Month
  982.   mov  al,Day
  983.   mov  [ebx],eax
  984. .data
  985. YearCurr  dw 2005
  986. MonthCurr db 04
  987. DayCurr   db 13
  988. .code
  989.   mov  ax,YearCurr
  990.   shl  eax,16
  991.   mov  ah,MonthCurr
  992.   mov  al,DayCurr
  993.   mov  [edx],eax
  994. ; Записываем новый код подсчета CRC
  995.   invoke WriteFile,IniDescr,offset Data_@@NewCRCProc,(026D515Bh-026D510Ch),offset bWrite,NULL
  996.   cmp  dword ptr bWrite,(026D515Bh-026D510Ch)
  997.   jz  @@PatchCRCOK
  998. .data
  999. DestFileNotPatchedCRCMsg db "xsystem.dll (CRC) not patched: I/O error !",0Ah,0
  1000. .code
  1001.   push offset DestFileNotPatchedCRCMsg
  1002.   call Write_Log
  1003. @@PatchCRCOK:
  1004.  
  1005. ;
  1006. ; Записать декриптор2 в незашифрованном виде (в 026D540Ch)
  1007. ;
  1008.  
  1009. ; Встать в нужное место
  1010.   invoke SetFilePointer,IniDescr,0540Ch,NULL,FILE_BEGIN
  1011.   invoke WriteFile,IniDescr,offset @@Decryptor2,(5440h - 540Ch),offset bWrite,NULL
  1012.   cmp  dword ptr bWrite,(5440h - 540Ch)
  1013.   jz   @@WriteDec2OK
  1014. .data
  1015. DestFileNotWriteDec2Msg db "xsystem.dll: decryptor2 not writed I/O error !",0Ah,0
  1016. .code
  1017.   push offset DestFileNotWriteDec2Msg
  1018.   call Write_Log
  1019. @@WriteDec2OK:
  1020.  
  1021. ;
  1022. ; Записать переход на новый декриптор (2)
  1023. ;
  1024.  
  1025. ; Встать в нужное место
  1026.   invoke SetFilePointer,IniDescr,053A2h,NULL,FILE_BEGIN
  1027.   mov  ebp,offset @@NewJumpAt53A2Done - offset @@NewJumpAt53A2
  1028.   invoke WriteFile,IniDescr,offset @@NewJumpAt53A2,ebp,offset bWrite,NULL
  1029.   cmp  dword ptr bWrite,ebp
  1030.   jz   @@WriteDecJump2OK
  1031. .data
  1032. DestFileNotWriteDecJump2Msg db "xsystem.dll: decryptor2(jump) not writed I/O error !",0Ah,0
  1033. .code
  1034.   push offset DestFileNotWriteDecJump2Msg
  1035.   call Write_Log
  1036. @@WriteDecJump2OK:
  1037.  
  1038. ; ***
  1039. ; Выход из программы: закрытие дескрипторов и т.п.
  1040. ; ***
  1041. Exit:
  1042. .data
  1043. EndWorkMsg db 0Ah,"All done... ;)",0Ah,0
  1044. .code
  1045.   push offset EndWorkMsg
  1046.   call Write_Log
  1047. ; Закрыть открытые файлы
  1048.   cmp  dword ptr IniDescr,-1
  1049.   jz   @@SkipCloseIniFile
  1050.   invoke CloseHandle,IniDescr
  1051. @@SkipCloseIniFile:  
  1052. ; Закрыть лог
  1053.   invoke CloseHandle,LogDescr
  1054.   invoke ExitProcess,0
  1055. ; ***
  1056. ; Подпрограмма записи в лог и на консоль конца строки (0Ah)
  1057. ; ***
  1058. Write_EndStr_ToLog proc
  1059. .data
  1060. EndStr db 0Ah,0
  1061. .code
  1062.   push offset EndStr
  1063.   call Write_Log
  1064.   ret
  1065. Write_EndStr_ToLog endp
  1066. ; ***
  1067. ; Подпрограмма записи в лог и на консоль одновременно
  1068. ; ***
  1069. ; [ebp+8] - адрес строки для записи
  1070. Write_Log proc
  1071.   push ebp
  1072.   mov  ebp,esp
  1073. ; Получить длину строки
  1074.   mov  esi,dword ptr [ebp+8] ; Адрес строки
  1075.   call Str_Len
  1076.   test ecx,ecx
  1077.   jz   @@ExitWrite_Log
  1078. ; Запись на консоль
  1079.   push ecx
  1080.   push NULL
  1081.   push offset bWrite
  1082.   push ecx ; Длина строки
  1083.   push dword ptr [ebp+8] ; Адрес строки
  1084.   push hConsole
  1085.   call WriteFile
  1086.   pop  ecx
  1087. ; Запись в протокол  
  1088.   push NULL
  1089.   push offset bWrite
  1090.   push ecx ; Длина строки
  1091.   push dword ptr [ebp+8] ; Адрес строки
  1092.   push LogDescr
  1093.   call WriteFile
  1094. @@ExitWrite_Log:  
  1095.   pop  ebp
  1096.   ret  4
  1097. Write_Log endp
  1098. ; ***
  1099. ; Get lenght of string
  1100. ; esi=addr of string; result: ecx=length
  1101. ; ***
  1102. Str_Len proc
  1103.   xor  ecx,ecx
  1104.   push eax
  1105.   push esi
  1106. @@GetStrLen:
  1107.   lodsb
  1108.   test al,al
  1109.   jz   @@ExitStrLen
  1110.   inc  ecx
  1111.   jmp  @@GetStrLen  
  1112. @@ExitStrLen:
  1113.   pop  esi
  1114.   pop  eax
  1115.   ret
  1116. Str_Len endp
  1117. ; ************* DecChar: Подпрограмма форматирования строки из числа ***********
  1118. ; eax=number to digit, edi=offset result string in format 00000000(@n[ebx])
  1119. ; ebx=начальный делитель
  1120. DecChar proc
  1121.   pushad
  1122.   pushfd
  1123.   cld
  1124. @GetDec:
  1125.   xor  edx,edx
  1126.   div  ebx
  1127.   add  al,'0'
  1128.   stosb
  1129.   push edx
  1130.   mov  eax,ebx
  1131.   xor  edx,edx
  1132.   mov  ebx,10
  1133.   div  ebx
  1134.   mov  ebx,eax
  1135.   pop  eax
  1136.   test ebx,ebx
  1137.   jnz  @GetDec
  1138.   popfd
  1139.   popad
  1140.   ret
  1141. DecChar endp
  1142.  ...
© Chingachguk / HI-TECH

0 1.118
archive

archive
New Member

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