Исследование программы 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(). Но она тоже контролируется нашим кодом, который просто делает возврат с коррекцией стека. После всего этого запускается защищенная программа.
Для тех, кто не понял, привожу указанное выше вкратце:
- Загружается 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 [Контейнер для процедуры диалога]
|
Заключение
Так сдалась эта действительно сильная защита, технология работы которой, как я сильно подозреваю, была позаимствованна у вирусов.
|