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

Целью нашего сегодняшнего исследования будет Ulead Gif Animaton v3.0, созданный фирмой Ulead. Программа защищена с помощью 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(). Но она тоже контролируется нашим кодом, который просто делает возврат с коррекцией стека. После всего этого запускается защищенная программа.

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


*

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


*

Наш обработчик ждет окончания распаковки и проверки, и ставит hook на RaiseException() и DialogBoxParamA().


*

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


*

Новый обработчик 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 [Контейнер для процедуры диалога]

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

 


Образование на Куличках