Delphi World - это проект, являющийся сборником статей и малодокументированных возможностей  по программированию в среде Delphi. Здесь вы найдёте работы по следующим категориям: delphi, delfi, borland, bds, дельфи, делфи, дэльфи, дэлфи, programming, example, программирование, исходные коды, code, исходники, source, sources, сорцы, сорсы, soft, programs, программы, and, how, delphiworld, базы данных, графика, игры, интернет, сети, компоненты, классы, мультимедиа, ос, железо, программа, интерфейс, рабочий стол, синтаксис, технологии, файловая система...
Исследование программы Ulead Gif Animator v3.0
В центре Москвы очередная бандитская разборка. С братками, джипами, автоматной перестрелкой. Программист бросается на тротуар и машинально на асфальте: "IDDQD, IDDQD".

Введение

Целью нашего сегодняшнего исследования будет Ulead Gif Animaton v3.0. Программа защищена с помощью Vbox v4.10. Защитные алгоритмы реализованы в трех DLL: Vboxp410.dll, Vboxb410.dll и Vboxt410.dll (или Vboxc410.dll – в "коммерческой" версии). Все эти библиотеки , за исключением первой, упакованны, поэтому все модификации мы будем вносить в Vboxp410.dll.

Как осуществляется защита? Тело программы модифицируется так, что сначала вызывается процедура проверки, и, после ее прохождения, происходит переход на реальную точку входа программы, если проверка прошла успешно, или на выход, если отведенное для тестирования программы время истекло. В теле нашей программы это выглядит так (дизассемблерные листинги взяты из WinDASM'а):


* Reference To: vboxp410., Ord:0001h
                                  |
:004A8020      Call dword ptr [004A8290]
:004A8026      push FFFFFFFF
:004A802B      call eax
:004A802D      ret 000C

Первое, что приходит на ум – "подсмотреть" нужное значение регистра EAX, и, подкорректировав стек, сделать переход (jmp) через процедуру проверки. Попробуйте… Не вышло? Это потому, что защита, кроме процедуры проверки, осуществляет восстановление заголовка и секций файла. Значит, нужно исследовать защиту. Думаю, многие пробовали это и до меня, но заканчивали с плачевным результатом. Защита действительно сильная. Модули проверяются на изменения в файле на диске (CRC), мало того, производится еще и проверка "развернутого" кода в памяти после запуска. Именно проверка кода в памяти и реагирует на установки контрольных точек (bpx) в SoftICE, ведь реально SoftICE заменяет код по нужному адресу на int 3 и выполняет i3here on. Следовательно, обычные контрольные точки применять нельзя – нарушим целостность кода в памяти.

Исследование

При запуске защищенной программы появляется окно, сообщающее об оставшемся времени. Находится это окно по адресу 070025C3 в Vboxt410.dll (которая нам недоступна) – поставьте в SoftICE контрольную точку bpmb 070025C3 x (это наша замена bpx) – и попадете в функцию DialogBoxParamA(). Вы убедитесь, что при нажатии кнопки "Quit" содержимое регистра ЕАХ будет равно 1, а при нажатии кнопки "Try" – нулю. Переведите часы на месяц вперед, и снова запустите программу. Нажав кнопку "Quit" вручную измените содержимое регистра ЕАХ на 0, и продолжите выполнение программы. Появится стандартный MessageBox с сообщением, что испытательный срок работы истек, и программа закрывается. Происходит это по адресу 7035629 (KERNEL32!EnterCriticalSection). Если же вместо вызова функции (сall) сделать переход через него с соответствующей коррекцией стека – программа будет работать. Попробуем проделать то же самое с DialogBoxParamA(), и Вы увидите, что здесь это не поможет – защита проверяет, выполнялась ли данная процедура, или нет, и реагирует соответствующим образом.

Итак, наша цель достигнута, но только в работающей программе с помощью SoftICE. Теперь нам необходимо сделать эти изменения постоянными, что является более трудной задачей.

Обозначим наши цели:

  • Изменить DialogBoxParamA() – иммитировать нажатие кнопки "Try".
  • Выполнить обход процедуры KERNEL32!EnterCriticalSection.
  • Скрыть наши действия от программных проверок.

Проделывать мы все это будем с Vboxp410.dll, и начнем с ее заголовка. Запускаем ProcDump, нажимаем кнопку "PE Editor", и открываем нашу DLL. Нажимаем кнопку "Sections". В описании характеристик секции .text мы видим 60000020, что означает Code, Executable, Readable . Изменяем это значение на E0000020: нажимаем правой кнопкой мыши на .text, и выбираем пункт "Edit Section". Теперь секция .rdata, значение характеристик равно 40000040, что означает Initialized Data, Readable. Изменяем это значение на C0000040. Этим самым мы изменили параметр файла Readable (только чтение) на свободный доступ – чтение и запись. Если бы мы этого не сделали, то при работе нашего модуля, который будет изменять свой же код, мы бы получили ошибку Invalid Page Fault. Далее перед нами стоит задача найти свободное место для записи нашего кода. Эта DLL создана с помощь компилятора С++, который включает в код кучу ненужного мусора. Свободное место нашлось, начиная с адреса 5021918.

Теперь описание нашей процедуры:


VBOXP410.DLL , точка входа:
5001F99  E97AF90100           jmp 05021918

Процедуры обработки:
5021918  C705991F0005B8960402 mov dword ptr [05001F99],020496B8
5021922  C6059D1F000505       mov byte ptr [05001F9D],05
5021929  A1AOB50205           mov eax,[0502B5A0]
502192E  C705AOB5020554190205 mov dword ptr [0502B5A0],05021954
5021938  A344190205           mov [05021944],eax
502193D  E95706FEFF           jmp 05001F99
5021942  0000
5021944  0000
5021946  0000
5021948  0000
502194A  0000
502194C  0000
502194E  0000
5021950  0000
5021952  0000
5021954  66FF0542190205       inc word ptr [05021942]
502195B  66813D421902050040   cmp word ptr [05021942],4000
5021964  742B                 jz  05021991
5021966  66813D42190205002E   cmp word ptr [05021942],2EOO
502196F  7534                 jnz 050219A5
5021971  C70590890507AB190205 mov dword ptr [07058990],050219AB
502197B  A1348A0507           mov eax,[07058A34]
5021980  A348190205           mov [05021948],eax
5021985  C705348A0507AE190205 mov dword ptr [07058A34],050219AE
502198F  EB14                 jmp 050219A5
5021991  A1E8760808           mov eax,[080876E8]
5021996  A348190205           mov [05021948],eax
502199B  C705E8760808AE190205 mov dword ptr [080876E8],050219AE
50219A5  FF2544190205         jmp [05021944]
50219AB  C21000               ret 0010
50219AE  8B442410             mov eax,[ESP+10]
50219B2  A34C190205           mov [0502194C],eax
50219B7  C7442410E1190205     mov dword ptr [esp+10],050219E1
50219BF  8F0550190205         pop dword ptr [05021950]
50219C5  68D0190205           push 050219DO
50219CA  FF2548190205         jmp [05021948]
50219DO  33CO                 xor eax, eax
50219D2  66C70554190205EB4F   mov word ptr [05021954],4FEB
50219DB  FF2550190205         jmp [05021950]
50219E1  837C240818           cmp dword ptr [ESP+08],18
50219E6  7510                 jnz 050219F8
50219E8  C744240811010000     mov dword ptr [ESP+08],00000111
50219FO  C744240C95040000     mov dword ptr [esp+OC],00000495
50219F8  FF254C190205         jmp [0502194C]

С точки входа (5001F99) переход осуществляется на нашу процедуру модификации. После загрузки DLL этот переход будет заменен оригинальным кодом, что сохранит нормальный вид DLL для прохождения программных проверок. Также наш код как бы ставит hook на вызов EnterCriticalSection(), и заменяет его на наш, новый обработчик. Этот обработчик ждет, пока не распакуется Vboxt410.dll, и после этого перенаправляет вызовы RaiseExeption() и DialogBoxParamA() на наши обработчики. Наш обработчик вызова RaiseException() представляет собой команду RET 10 – немедленный возврат с коррекцией стека. А вот обработчик DialogBoxParamA() немного сложнее: он вносит в стек значения, эмулирующие нормальный возврат из нормальной процедуры DialogBoxParamA(), и перехватывает процедуру передачи сообщений на диалог, подменяя ее своим обработчиком. Этот обработчик ждет, пока в окно не будет посланно сообщение WM_SHOWWINDOW, и заменяет его сообщением закрытия окна. После чего в регистр ЕАХ записывается ноль, и обработчик изолируется, записывая в свое начало команду безусловного перехода (jmp) на настоящий Critical_Handler. После чего мы выходим из нашего обработчика обратно в защиту, которая пытается показать окно с сообщением об истечении срока действия программы (DialogBoxParamA), контроль над которым осуществляет наш код – окно лишь промелькнет на экране. После этого вызывается API-функция RaiseException(). Но она тоже контролируется нашим кодом, который просто делает возврат с коррекцией стека. После всего этого запускается защищенная программа.

Для тех, кто не понял, привожу указанное выше вкратце:

  1. Загружается Vboxp410.dll, сразу же просходит безусловный переход на наш обработчик, который восстанавливает измененный код в точке входа (то место, откуда был JMP), и ставит HOOK на процедуру EnterCriticalSection() – для ожидания распаковки Vboxt410.dll.
  2. Наш обработчик ждет окончания распаковки и проверки, и ставит hook на RaiseException() и DialogBoxParamA().
  3. Новый обработчик диалога инициирует его закрытие, выставляет значения, необходимые для корректной работы программе.
  4. Новый обработчик RaiseException() осуществляет возврат, не производя никаких действий.

Ниже приведен тот же код в "структурном" виде:


Точка входа:
jmp Восстановление кода

Восстановление кода:
mov dword ptr [Точка входа],020496B8 – восстановление точки входа
mov byte ptr [Точка входа+4],05      – восстановление точки входа
mov eax,[KERNEL32!EnterCriticalSection]
mov dword ptr [KERNEL32!EnterCriticalSection], Новый обработчик EnterCriticalSection
– подмена обработчика
mov [Временный контейнер для EnterCriticalSection],eax
jmp Точка входа

Cчетчик:
dw 0
Временный контейнер для EnterCriticalSection:
dd 0
Контейнер для диалога:
dd 0
Контейнер для процедуры диалога:
dd 0
Контейнер возврата из диалога:
dd 0

Новый обработчик EnterCriticalSection:
inc word ptr [Счетчик]
cmp word ptr [Cчетчик],4000
jz Hook DialogBox для VBOXC410 – подмена обработчика DialogBoxParamA после распаковки
cmp word ptr [Cчетчик],2EOO
jnz Реальный обработчик EnterCriticalSection – еще не распакованна, обрабатывается
"родным" обработчиком

Обработчики для VBOXT410:
mov dword ptr [KERNEL32!RaiseException], Новый обработчик RaiseException
– подмена обработчика
mov eax,[USER32!DialogBoxParamA]
mov [Контейнер для диалога],eax
mov dword ptr [USER32!DialogBoxParamA], Указатель на новый обработчик
- подмена обработчика
jmp  настоящий EnterCriticalSection

Обработчик для VBOXC410:
mov eax,[USER32!DialogBoxParamA]
mov [Контейнер диалога],eax
mov dword ptr [USER32!DialogBoxParamA], Указатель на новый обработчик диалога
– подмена обработчика
Настоящий EnterCriticalSection:
jmp [Контейнер для EnterCriticalSection]

Новый обработчик RaiseException:
ret 10

Новый обработчик диалога:
mov eax,[esp+1O]
mov [Контейнер процедуры диалога],eax
mov dword ptr [esp+1O],Новый обработчик процедуры диалога
pop dword ptr [Возврат из диалога]
push  Call_возврат из диалога
jmp [Контейнер для диалога]

Call_возврат из диалога:
xor eax,eax
mov word ptr [Новый обработчик EnterCriticalSection],4feb
jmp [Возврат из диалога]

Указатель на новую процедуру диалога:
cmp dword ptr [esp+08],18
jnz Указатель на настоящую процедуру диалога
mov dword ptr [esp+08],111
mov dword ptr [esp+OC],495

Указатель на настоящую процедуру диалога:
jmp [Контейнер для процедуры диалога]

Заключение

Так сдалась эта действительно сильная защита, технология работы которой, как я сильно подозреваю, была позаимствованна у вирусов.

Проект Delphi World © Выпуск 2002 - 2024
Автор проекта: USU Software
Вы можете выкупить этот проект.