Здесь я расскажу о нескольких хитростях, которые можно использовать для защиты своих вирусов и/или своих программ против отладчиков всех уровней, уровня приложения и системы. Я надеюсь, что вам понравится эта статья.
Этой функции нет в Win95, поэтому вам следует вначале выяснить, существует ли она, и работает только с отладчиками уровня приложения (таким как TD32). Она работает прекрасно. Давайте посмотрим, что написано о ней в справочнике по Win32 API.
Функция IsDebuggerPresent показывает, запущен ли вызывающий ее процесс в контексте отладчика. Эта функция экспортируется из KERNEL32.DLL.
BOOL IsDebuggerPresent(VOID)
- Аргументы
У этой функции нет аргументов.
- Возвращаемое значение
Если текущий процесс запущен в контексте отладчика, возвращаемое значение не равно нулю.
Если текущий процесс не запущен в контексте отладчика, возвращаемое значение равно нулю.
Пример, демонстрирующий эту функцию очень прост. Вот он:
;---[ CUT HERE ]-------------------------------------------------------------
.586p
.model flat
extrn GetProcAddress:PROC
extrn GetModuleHandleA:PROC
extrn MessageBoxA:PROC
extrn ExitProcess:PROC
.data
szTitle db "IsDebuggerPresent Demonstration",0
msg1 db "Application Level Debugger Found",0
msg2 db "Application Level Debugger NOT Found",0
msg3 db "Error: Couldn't get IsDebuggerPresent.",10
db "We're probably under Win95",0
@IsDebuggerPresent db "IsDebuggerPresent",0
K32 db "KERNEL32",0
.code
antidebug1:
push offset K32 ; Получаем адрес базы KERNEL32
call GetModuleHandleA
or eax,eax ; Проверяем на ошибки
jz error
push offset @IsDebuggerPresent ; Теперь проверяем, существует
push eax ; ли IsDebuggerPresent. Если
call GetProcAddress ; GetProcAddress возвращает
or eax,eax ; ошибку, мы считаем, что мы
jz error ; в Win95
call eax ; Вызываем IsDebuggerPresent
or eax,eax ; Если она возвращает не 0,
jnz debugger_found ; нас отлаживают
debugger_not_found:
push 0 ; Показываем "Debugger not found"
push offset szTitle
push offset msg2
push 0
call MessageBoxA
jmp exit
error:
push 00001010h ; Show "Error! We're in Win95"
push offset szTitle
push offset msg3
push 0
call MessageBoxA
jmp exit
debugger_found:
push 00001010h ; Show "Debugger found!"
push offset szTitle
push offset msg1
push 0
call MessageBoxA
exit:
push 00000000h ; Exit program
call ExitProcess
end antidebug1
;---[ CUT HERE ]-------------------------------------------------------------
Не правда ли, это красиво? Micro$oft сделал эту работу за нас :). Но, конечно, не ждите, что этот метод будет работать с SoftIce'ом, богом отладки.
Если вы смотрели статью "Wub95 Structure and Secrets", которая была написана Murkry/iKX, и опубликована в Xin-3, вы сообразите, что в регистре FS находится очень крутая структура. Давайте взглянем в поле FS:[20h]... Это 'DebugContext'. Всего лишь сделаем следующее:
mov ecx,fs:[20h]
jecxz not_being_debugger
[...]
<--- делайте, что хотите, нас отлаживают :)
Потому, если FS:[20h] равен нулю, нас не отлаживают. Наслаждайтесь этим маленьким и простым методом для обнаружения отладчиков! Конечно, это не сработает против SoftICE...
Я не знаю почему, но отладчики уровня приложения умирают, если программа использует SEH. Эмуляторы код, если мы делаем ошибки, умирают тоже :). SEH, как я писал в своей статье для DDT#1, используется для многих интересных целей. Хорошо, так как я не хочу рассказывать все это заново, я просто скопирую и вставлю мое старое описание :).
--[DDT#1.2_6]---------------------------------------------------------------
Хорошо, это очень простой туториал о Structured Exception Handler. Когда я увидел SEH, реализованный в вирусе, я подумал "Хорошо, это большая работа. Наверное, это было сложно реализовать". Поэтому я просто пропустил это. Но, как только мой Destiny сделал General Protection Fault'ы под NT, я понял, что я должен сделать что-то. И SEH - это единственный путь. Хорошо, мы можем сделать этоу очень сложным или очень простым для понимания. Конечно, я предпочитаю сделать это попроще :).
Установка фрейма SEH Сначала мы сохраняем его для нашей собственной безопасности простой строкой кода.
А теперь надо установить указатель на наш обработчик (например, представьте, что мы используем call для вызова настройки SEH, а наш обработчик идет непосредственно после этой инструкции call: мы можем использовать смещение ret).
push offset SEH_Handler
mov fs:[0],esp
Ок, просто как только возможно. Что насчет восстановления исходного SEH. Еще проще. Просто вызовите инструкцию, обратную первой.
Удивительно, что может сделать эта простая для реализации вещь для наших вирусов. Для меня (и мне было важно именно это использование SEH) наиболее главным было то, что я смог избежать всех этих отвратительных голубых экранов, когда мы запускаем наш Win95-вирус в среде NT.
% Пример использования SEH %
Мы можем скомпилировать данный пример следующим образом:
tasm32 /m3 /ml sehtest,,;
tlink32 /Tpe /aa sehtest,sehtest,,import32.lib
;---[ CUT HERE ]-------------------------------------------------------------
.386p
.model flat ; 32 бита рулят
extrn MessageBoxA:PROC ; Определенные API
extrn ExitProcess:PROC
.data
szTitle db "Structured Exception Handler example",0
szMessage db "Intercepted General Protection Fault!",0
.code
start:
call setupSEH
exceptionhandler:
mov esp,[esp+8] ; Ошибка дает нам старый ESP
; в [ESP+8]
push 00000000h ; Аргументы для MessageBoxA
push offset szTitle
push offset szMessage
push 00000000h
call MessageBoxA
push 00000000h
call ExitProcess ; Выходим из приложения
setupSEH:
push dword ptr fs:[0] ; Push'им оригинальный
; обработчик SEH
mov fs:[0],esp ; И помещаем новый (который
; находится после первого
; call)
mov ebx,0BFF70000h ; Пытаемся писать в ядро (что
mov eax,012345678h ; вызовет исключение)
xchg eax,[ebx]
end start
;---[ CUT HERE ]-------------------------------------------------------------
[...]
--[DDT#1.2_6]---------------------------------------------------------------
Я надеюсь, что вы поняли все это. Если нет... Ладно, забудьте об этом :). Также как и другие методы, представленные выше, он не работает по отношению к SoftICE.
Здесь я должен поблагодарить Super/29A, потому что именно он рассказал мне об этом методе. Я разделил его на две части: в этой мы рассмотрим, как использовать его из вирусов Ring-0. Я не буду помещать здесь программу-пример целиком, потому что она займет лишнее место, но вы должны знать, что этот метод должен выполняться в Ring-0, а VxDCall должен быть восстановлен из-за проблемы обратного вызова (вы помните о ней?).
Ок, мы будем использовать сервис менеджера виртуальных машин (VMM) Get_DDB, поэтому сервис будет 00010146h (VMM_Get_DDB). Давайте посмотрим, что говорится об этом сервисе в SDK.
mov eax, Device_ID
mov edi, Device_Name
int 20h ; VMMCall Get_DDB
dd 00010146h
mov [DDB], ecx
Определяет, установлен или нет VxD определенного устройства, и если установлен, возвращает DDB для этого устройства.
Использует ECX, флаги.
Возвращает DDB для определенного устройства, если функция была выполнена успешно.
В противном случае возвращает ноль.
Device_ID: Идентификатор устройства. Этот параметр может быть равен нулю для именованных устройств.
Device_Name: Восьмибуквенное имя устройства, которое дополнено (в случае необходимости) пустыми символами. Этот параметр требуется только тогда, когда Device_ID равен нулю. Имя устройства чувствительно к регистру.
Ладно, вы наверняка удивляетесь, о чем идет разговор. Очень просто, поле Device_ID VxD SoftICE постоянно для всех программ, а так как оно зарегистрировано в Micro$oft'е, у нас есть оружие против этого отладчика. Его Device_ID всегда равно 202h. Поэтому мы можем использовать следующий код:
mov eax,00000202h
VxDCall VMM_Get_DDB
xchg eax,ecx
jecxz NotSoftICE
jmp DetectedSoftICE
Где NotSoftICE должно быть продолжением кода вируса, а метка DetectedSoftICE должна обрабатывать тот случай, если наш враг жив :). Я не предполагаю здесь никакой деструкции, так как, например, на моем компьютере SoftICE всегда активен :).
Ладно, теперь идет другой метод для обнружения моего возлюбленного SoftICE'а, но основанный на той же самой идее, что и выше: 202h ;). Снова я должен поблагодарить Super :). Ладно, в Ralph Brown Interrupt list мы можем найти очень крутой сервис: прерывание 2Fh, функция 1684h.
На входе:
AX = 1684h
BX = virtual device (VxD) ID (see #1921)
ES:DI = 0000h:0000h
На выходе:
ES:DI -> входная точка VxD API, или 0:0, если VxD не поддерживает этот
API.
N.B.: некоторые виртуальные устройства в улучшенном режиме Windows
предоставляют сервисы, которые могут использовать приложения.
Например, Virtual Display Device предоставляет API, используемый
WINOLDAP.
Поэтому вы поместите 202h в BX, запустите эту функцию. А потом скажете... "Эй, Билли... Через какое место я могу использовать прерывания?". Мой ответ... ИСПОЛЬЗУЙТЕ VxDCALL0!!!
И наконец, чудесный трюк, которого вы ждали... Универсальный способ найти SoftICE и Win9x и в WinNT! Это очень просто, 100% основанно на API и без всяких "грязных" трюков, которые не идут на пользу совместимости. И ответ находится не так далеко, как вы думаете... ключ заключается в API, который вы уже использовали раньше: CreateFile. Да, именно эта функция... Разве это не прекрасно? Ладно, мы можем попытаться открыть следующее:
- SoftICE для Win9x : ".SICE"
- SoftICE для WinNT : ".NTICE"
Если функция возвращает нам что-нибудь, отличное от -1 (INVALID_HANDLE_VALUE), SoftICE запущен! Далее следует демонстрационная программа:
;---[ CUT HERE ]-------------------------------------------------------------
.586p
.model flat
extrn CreateFileA:PROC
extrn CloseHandle:PROC
extrn MessageBoxA:PROC
extrn ExitProcess:PROC
.data
szTitle db "SoftICE detection",0
szMessage db "SoftICE for Win9x : "
answ1 db "not found!",10
db "SoftICE for WinNT : "
answ2 db "not found!",10
db "(c) 1999 Billy Belcebu/iKX",0
nfnd db "found! ",10
SICE9X db ".SICE",0
SICENT db ".NTICE",0
.code
DetectSoftICE:
push 00000000h ; Проверяем наличии
push 00000080h ; SoftICE для среды Win9x
push 00000003h
push 00000000h
push 00000001h
push 0C0000000h
push offset SICE9X
call CreateFileA
inc eax
jz NoSICE9X
dec eax
push eax ; Закрываем открытый файл
call CloseHandle
lea edi,answ1 ; SoftICE найден!
call PutFound
NoSICE9X:
push 00000000h ; А теперь пытаемся открыть
push 00000080h ; SoftICE для WinNT...
push 00000003h
push 00000000h
push 00000001h
push 0C0000000h
push offset SICENT
call CreateFileA
inc eax
jz NoSICENT
dec eax
push eax ; Закрываем хэндл файла
call CloseHandle
lea edi,answ2 ; SoftICE для WinNT найден!
call PutFound
NoSICENT:
push 00h ; Показываем MessageBox с
push offset szTitle ; результатами
push offset szMessage
push 00h
call MessageBoxA
push 00h ; Завершаем программу
call ExitProcess
PutFound:
mov ecx,0Bh ; Меняем "not found" на
lea esi,nfnd ; "found"; адрес, где нужно
rep movsb ; изменить, находится в EDI
ret
end DetectSoftICE
;---[ CUT HERE ]-------------------------------------------------------------
Это действительно работает, поверьте мне :). Тот же метод можно применить к "враждебным" драйверам, просто проведите небольшое исследование.
Ладно, вот я и рассказал о нескольких простых антиотладочных трюках. Я надеюсь, что вы сможете использовать их в своих вирусах без проблем. Пока!