Virus 版 (精华区)

发信人: Kernel (好冷), 信区: Virus
标  题: 瞎说病毒调试技术
发信站: 哈工大紫丁香 (Tue Dec  2 23:10:24 2003), 站内信件


这是一篇未完成的文章,基本部分还算完善,但后面的则只有纲要了。我没太多的精力和
时间完成这篇文章。如果我以后要写,我想写一个比较全面介绍蠕虫编制技术的,但只是
一个想法,也不知道是否有心情、精力和时间去实现。

本文主要不是介绍调试工具,而是介绍一些调试病毒的技巧和经验。
写程序,调试很重要。调试病毒其实就是调试程序,唯一的不同在于调试正常程序时你可
以任意运行程序,但调试病毒则不行,如果你做好了一个release版,相信你自己也不敢在
你自己的机器上运行。

首先说说一些基本的调试方法和经验,以及应该注意的地方。

1,分步调试,在一个模块还有bug的时候绝不要开始写新的模块。一个完整的病毒可能会
给你的机器带来很大的伤害,在病毒获得生命之前,精心调试她的肢体是个很不错的主意
,同时这也是进行局部优化的一个大好时机。比如你写了一个感染模块,好的,先就调试
她,直到她可以成功的把你现在的伪病毒(部分病毒代码+部分调试用的辅助代码)与宿主
相结合为止。然后暂时抛弃她,埋头写其它部分,直到最后把她和整个病毒结合在一起,
再综合调试。千万不要因为你激动于有了新的想法而急着付诸于实现,如果你一口气写完
整个病毒,那么调试将是非常痛苦的事。

2,OutputDebugString。现在的调试工具已经相当发达,但有时古老的“printf”打印信
息的技术还是非常有用的。在Windows下,这个“printf”就是OutputDebugString(MFC里
可用TRACE,本质还是这东西),用它可以在调试工具的输出窗口上列出信息,可以告诉你
病毒现在执行到哪里了。用惯了Delphi、VC强大调试功能的人可能还不很理解这种古老技
术的魅力,在你为调试一个bug一筹莫展的时候,试试它吧。
注意OutputDebugString是把字符串送往调试器,所以要有一个“调试器”监视你的病毒来
截获这些字符串,你当然可以用Delphi或VC attach到你的病毒进程,但更好的选择也许是

A,装一个SoftIce,在需要看输出信息时,Ctrl+D呼叫出SI,在它的输出窗口里查看。

B,下载一个DbgView,然后运行它,它就能俘虏所有的调试信息。这是最好的方法。

3,Dummy File。在调试阶段,显然不能让你的病毒在你的机器上胡乱寻找文件进行感染,
要限制它的动作。在感染模块里加上几行代码,让它判断文件名,只感染特定文件,比如
只感染damn开头的文件。对于Dummy File的选择我们要慎重考虑。如果你只用一个文件来
测试,那么恭喜你,你的病毒在实战中十有八九会经常崩溃。同为PE文件,Borland和Mic
roshit的编译器产生的就大不同(即使同是Borland的编译器,Delphi和BCB之间也有差别
。BCB生成的.exe有一个export table,比较特殊,Delphi生成的则没有),不注意其中的
差别,你的病毒将很虚弱,不堪一击。你需要找各种类型的文件来试验,Delphi程序,BC
B程序,VC程序,TASM程序,SFX程序,等等,都应该是你试验的对象。还要试试你的病毒
不能感染但可能检查的程序,比如DOS程序,我以前的一个病毒(Win32.Loicer)就因为只试
验有效的PE病毒而bug累累 

4,不只怀疑自己。当你对几行代码进行几天的测试后确信你的代码是正确的但却还是出错
的时候,你可能要怀疑操作系统本身。国外的一些汇编高手(非病毒高手)也忍不住要大
骂MShit的人是猪,因为Windows可能会给你的程序带来意想不到的错误。你试试一些需要
处理字符串的API,在调用它们前先把DF置1(用std指令),然后调用,看看………………
……。为了不遭到M$的骚扰,编码时就要非常仔细,在调用API时要确保Flag基本被M$认可
,比如DF,CF清0,主要是DF。

5,关于调试代码。为了限制病毒在调试时的行为,我们可能需要加上一些调试代码,比如
判断文件名的代码,这些代码将不出现在release版中,只在调试时有用,所以我们要在r
elease版中去掉这些代码。对于这一点有两种方法,一是用条件汇编(条件编译)把这些
代码括起来,比如
if debug
mov eax,[esi]
cmp eax,'nmad'
jnz notinfect
endif
在relese版中只要把debug声明成debug = 0就行了。
二是在release版中把这些代码彻底删除,这样是最彻底最不容易出错的。我个人倾向于二
,但为了安全起见,我通常是二者都用。为了方便辨识调试代码,需要有一定的代码风格
。无论是汇编还是C,我们通常把代码进行缩进,所以为了辨识调试代码,可以把所有调试
代码都顶头写,这样就显得很突兀,很好辨认。在任何情况下保持良好的代码风格是一种
值得培养的习惯。

6,仔细阅读代码。即使你已经试了1000种情况都证明你的病毒是运行良好的,但不代表第
1001种情况你还能那么幸运。要想真正bug free是非常困难的,但我们可以争取把bug减小
到最少。你的病毒可能在“正常”环境中生活得很好,但极端情况呢?比如感染一个大小
为3G的PE文件会怎样?可能你不想或不能花费太多的时间在编写病毒和调试病毒上,所以
你不能做一些极端试验,那么如果你仍不想放过为已经测试良好的病毒找出bug的机会,仔
细阅读你的病毒代码是个很不错的选择。不管你相信不相信,人类的定式思维是很严重的
,不妨举个大家可能都经历过的例子。我在上学时,不是总要考试吗?我经常是答完以后
,检查一遍没发现错误就认为全都对了,然后就坐立不安,想睡觉,但感觉那样太对不起
考试,对不起自己,对不起人民………………:-),想再检查吧,又觉得自己对了,检查不
进去,就这样惶惑到铃声响起,最后也没得满分。说这些废话,只想说明一个问题,就是
你在阅读自己的代码时,如果还是读源代码,那么可能自我感觉良好,思维定式发生作用
,而且还有注释帮忙,检查不出什么的。所以最好换一种方式,用IDA反汇编你的病毒,然
后看反汇编的结果,当作一个别人写的病毒来看,应该可以比较容易看出问题,然后再在有问题的地方查阅源代码,确定是否是bug。当然这种方法对于汇编
病毒是有效的,但对于其它语言写的东西,还是看源代码吧 


基本的说完了,让我们再稍微进一步,具体看看一些病毒调试的情况
1,进程间调试。
在病毒运行的时候骚扰别的进程有时是非常有用且非常有趣的,这时就要跨进程调试了。
这里所说的跨进程调试,不是真的让你去调试宿主进程,那样还是很麻烦而且没必要的。
我们想要知道的结果通常就是看病毒在其它进程里的健康情况,以及精神是否愉快。这种
情况,没什么比OutputDebugString更有效的了。在插入的代码中放几条指令,调用Outpu
tDebugString来记录病毒的心跳,当你看到应该出现的信息都输出时,那么可以说插入进
程已经非常成功了。对于OutputDebugString的地址,可以用硬编码。int 3也是很好的手
段,它疗效显著,而且不像OutputDebugString那样需要很多字节去输出信息,只要一个字
节就够了。在插入代码中放几个int 3,然后保证SoftIce里的i3here为on,是个非常不错
的选择。

2,机器间调试
病毒发展的一个大趋势就是从单机感染开始转向跨机器感染,机器间的传播通道可能包括
LAN,Internet,无线设备,等等。在写这类病毒一定要仔细调试,在本地运行良好的病毒
可能在搜索局域网时就死翘翘了。

3,ring 0代码调试。我喜欢可以在最多机器上生存的Win32病毒,所以以ring 3为主,但
可能有人喜欢比较深入的技术,想玩ring 0病毒,所以这里稍微说一下ring 0的调试。想
要调试ring 0病毒,如果你在你自己的机器上调试,那么要做好牺牲的心理准备,大量的
blue screen会搞得你晕头转向。


--

※ 来源:.哈工大紫丁香 bbs.hit.edu.cn [FROM: 218.108.199.61]
[百宝箱] [返回首页] [上级目录] [根目录] [返回顶部] [刷新] [返回]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:2.075毫秒