DOS下COM病毒分析思路

也许有人会说,DOS已经是明日黄花了,怎么还讨论DOS病毒呢?这不是很落后吗?其实不然。病毒技术的鼎盛发展时期是在DOS年代——Poly Engine、Metamorphism等技术都是在DOS病毒中发展起来的,现在到了Windows时代,反而流传的是一些木马、蠕虫等没有多少技术含量的病毒,虽然不乏精品,但同dos时代的病毒相比,经典可就算不上了!(个人观点,不免偏激)。请记住:计算机技术从来都不会是一跃千里的,都有个循序渐进的发展过程,现在的PE病毒技术,大部分都是来源于古老的DOS年代。我们从DOS年代的COM病毒开始学习,有助于我们直接抓住问题的本质。好了,不多说废话了,让我们开始!

 

几个基本概念:
1, OM的文件格式
COM文件的格式——没有格式!它是直接加载到内存中的一个映像,从第一条语句开始,就是它的代码和数据,没有什么jjyy的格式在捣乱 :)方便、直接、简洁——这也是为什么我们选用COM病毒作为开始的原因。
2,病毒的重定位
在几乎每个感染型的病毒的开头都有这样的语句:
call nStart
nStart:
pop bp
sub bp, offset nStart
这些语句是用来干嘛的呢?好像是吃饱了饭没事干哦……其实不然。让我们来仔细考虑一下。当正常的COM程序执行时,它的基址一般是100h的,这个地址会由操作系统为你重定位,因此总是能保证程序被成功地装载运行。但是,如果病毒在宿主中插入了一段新的代码,假设它要从200h处开始执行,那么事情就没有那么简单了。因为宿主程序并没有预料到这段代码的存在,而操作系统也不可能为你修正这个偏移。因此我们就要自己进行重定位操作。上面的语句就是取得病毒在宿主中的实际偏移地址。Call 指令实际上是 push 和 jmp 的组合。当call nStart时,实际上是把call nStart的下一条指令(也就是pop ebp)的地址压入堆栈然后jmp到nStart,由于之前已经把pop bp的地址压入了堆栈,所以当真正执行到pop bp这条指令的时候,实际上就是把pop bp这条指令的地址放到了bp中。这样就得到了当前病毒代码的真正的偏移地址。这也是病毒中常用的手法。几乎无一例外。
3病毒的加密与解密
病毒为了防止被Aver轻易地杀死,它肯定要对自身进行一定程度的加密,以加大Aver分析的难度。通常,加密病毒由解密头和加密的代码组成。加密病毒运行时,先执行的是解密代码,对加密代码解密,然后执行刚解密的代码,也就是实现传播的主体代码。下面来看看常见的采用xor方式加密的病毒的框架:
mov cx, Virus_Size
mov di, offset Encrypt_Start
Encrypt_Loop:
xor byte ptr[di], key
inc di
loop Entrypt_Loop
Enctypt_Start:
; 加密后的代码
; …
可以看出,经过解密后,原先被加密的病毒体会被还原成正常的可以执行的代码,这样,病毒就可以继续往下执行了。

实例剖析:
准备工作做得差不多了,让我们来实际演练一下吧!下面的分析有点枯燥无味,请各位耐心点哦! :)
一、病毒的入口:
用IDA Pro打开病毒样本文件sample.vir,经过反汇编后,按下“C”,让IDA对反汇编结果进行分析,可以看到:
seg000:0100 E9 31 08 jmp loc_934
seg000:0100 ; ---------------------------------------------------------------------------
seg000:0103 E8 db 0E8h ; ?
seg000:0104 41 db 41h ; A
seg000:0105 00 db 0 ;
seg000:0106 47 db 47h ; G
seg000:0107 6F db 6Fh ; o
第一条指令就是跳到病毒体开始的地方,好,让我们双击“loc_934”,IDA会自动跳到该处:
seg000:0934 E8 00 00 call $+3
seg000:0937 5D pop bp
seg000:0938 81 ED 08 01 sub bp, 108h
seg000:093C 8D B6 28 01 lea si, [bp+128h]
seg000:0940 E8 04 00 call sub_947
seg000:0943 EB 12 jmp short loc_957
啊?怎么那么眼熟呢?请看,在地址0934h的指令是call $+3,而0937h的指令是pop bp,再接着是一条sub bp, 108h,这不是前面提到的很经典的病毒重定位语句吗?呵呵。在这里,由于call $+3的作用,导致pop bp之后,bp的值是0937h,然后bp再减去108h,也就是说,bp最后的值是082fh。
二、病毒的自身解密流程:
我们看到,在0940h处是一个call,嗯!有古怪!让我们双击sub_947,跟进去:
seg000:0945 3B db 3Bh ; ;
seg000:0946 00 db 0 ;
seg000:0947
seg000:0947 ; *************** S U B R O U T I N E ***************************************
seg000:0947
seg000:0947
seg000:0947 sub_947 proc near ; CODE XREF: seg000:0940 p
seg000:0947 8B 96 16 01 mov dx, [bp+116h]
seg000:094B 8B FE mov di, si
seg000:094D B9 EC 01 mov cx, 1ECh
seg000:0950
seg000:0950 loc_950: ; CODE XREF: sub_947+D j
seg000:0950 AC lodsb
seg000:0951 32 C2 xor al, dl
seg000:0953 AA stosb
seg000:0954 E2 FA loop loc_950
seg000:0956 C3 retn
seg000:0956 sub_947 endp
看看0947h处的指令:
mov dx, [bp+116h]
它的作用是把[bp+116h]处的值赋值给通用寄存器dx。
上面的分析中我们知道,bp的值是082fh,所以:
[bp+116h] = [082fh+116h] = [0945h]
往上看,0945h处的值是3Bh,因此dx的值就是3Bh。
好,我们再接着看:
mov di, si
从上面的语句中可以知道,si的值是:
lea si, [bp+128h]
我们来算一下:
[bp+128h] = [082fh+128h] = [957h]
0957h正好是 sub_947 endp 之后的第一条指令所在的地址,也就是相当于上面的病毒框架中的Entrypt_Start:
Enctypt_Start:
; 加密后的代码
; …
呵呵,聪明的你是不是已经看出来了呢?0957h开始的就是加密后的病毒代码,这里的mov di, si是为了下面的xor解密作准备。
mov cx, 1ECh 这条指令就不用多说了吧?它表示的是要解密的病毒体的长度。
好了,再接着就是用xor指令进行解密了,经过解密之后,病毒体还原成正常的指令,可以继续往下执行。
三、利用debug进行病毒体的解密:
细心的读者可能会问,病毒在运行的过程中,会自动把病毒进行还原,但是我们在IDA的反汇编窗口中,看到的还是未被还原前的加密代码啊,我们怎样才能够看到正常的代码呢?呵呵,解决的方法有两个:一个是用IDA Pro本身的IDC脚本进行解密,不过这种方法比较繁琐。另一个是用debug程序进行动态调试,得到还原后的程序,这也就是前面我们所说的“关子”。 :)
让我们用debug打开sample.vir,下面是解密还原的全过程,有点长:
c:/>debug sample.vir
-r
AX=0000 BX=0000 CX=0A43 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BA ES=13BA SS=13BA CS=13BA IP=0100 NV UP EI PL NZ NA PO NC
13BA:0100 E93108 JMP 0934
-t

AX=0000 BX=0000 CX=0A43 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000
DS=13BA ES=13BA SS=13BA CS=13BA IP=0934 NV UP EI PL NZ NA PO NC
13BA:0934 E80000 CALL 0937
-t

AX=0000 BX=0000 CX=0A43 DX=0000 SP=FFEC BP=0000 SI=0000 DI=0000
DS=13BA ES=13BA SS=13BA CS=13BA IP=0937 NV UP EI PL NZ NA PO NC
13BA:0937 5D POP BP
-t

AX=0000 BX=0000 CX=0A43 DX=0000 SP=FFEE BP=0937 SI=0000 DI=0000
DS=13BA ES=13BA SS=13BA CS=13BA IP=0938 NV UP EI PL NZ NA PO NC
13BA:0938 81ED0801 SUB BP,0108
-t

AX=0000 BX=0000 CX=0A43 DX=0000 SP=FFEE BP=082F SI=0000 DI=0000
DS=13BA ES=13BA SS=13BA CS=13BA IP=093C NV UP EI PL NZ AC PO NC
13BA:093C 8DB62801 LEA SI,[BP+0128] SS:0957=218F
-t

AX=0000 BX=0000 CX=0A43 DX=0000 SP=FFEE BP=082F SI=0957 DI=0000
DS=13BA ES=13BA SS=13BA CS=13BA IP=0940 NV UP EI PL NZ AC PO NC
13BA:0940 E80400 CALL 0947
-p

AX=0024 BX=0000 CX=0000 DX=003B SP=FFEE BP=082F SI=0B43 DI=0B43
DS=13BA ES=13BA SS=13BA CS=13BA IP=0943 NV UP EI PL NZ NA PE NC
13BA:0943 EB12 JMP 0957
-rcx
CX 0000
:0a43
-w
Writing 00A43 bytes
-q
让我来解释一下:
1、记录文件的原长度:
刚开始下的第一条命令是“r”,目的是记录下bx和cx的值——在DOS文件系统中,COM文件刚刚被装载进内存后,bx和cx记录的是文件本身的长度,其中,高位被记录在bx中,低位被记录在cx中。在这里,cx=0a43h, bx=0h
2、单步执行病毒体,让其自解密:
接着就是用“t”命令进行单步执行,目的是模拟病毒的正常执行程序,一直到了:
13BA:0940 E80400 CALL 0947
这个CALL是进行解密的CALL,所以我们要用“p”命令一直把它运行完,当这个CALL运行完后,病毒体就完成了解密过程,此时在内存中的病毒已经是还原了的了。
3、dump出还原后的病毒体:
然后,我们用“rcx”命令,把刚才的cx=0a43h还原,再接着用“w”命令把内存中的映像写入到文件sample.vir中去,这样,sample.vir就是还原的程序了。
四、清除病毒:
至此,COM病毒的还原工作已经做完,剩下的就是常规的病毒分析流程了。
俗话说:知己知彼,百战不殆!我们来想想,如果我是病毒的作者,我会让这个病毒做什么事情呢?首先,肯定是要保存宿主文件的原入口的第一条指令,然后修改宿主文件的入口指令,让第一条指令就跳到我们的病毒体去(很经典吧?:)),然后,在病毒体中,肯定会有跳回宿主原入口的指令,这样,才能达到病毒自身隐藏的目的。
1、找回宿主的原入口:
根据这个思路,我们可以在病毒体内寻找一些读文件的操作,这些操作中必有一个是读取保存在病毒体内的宿主的原入口的。并且在这些读文件的操作中,赋值给cx的值一般是3左右,因为入口指令一般都很小。
好,我们在解密了的病毒体内找到了这样的指令:
seg000:09D2 B4 3F mov ah, 3Fh ; '?'
seg000:09D4 8D 96 2C 02 lea dx, [bp+22Ch]
seg000:09D8 B9 03 00 mov cx, 3
seg000:09DB CD 21 int 21h ; DOS - 2+ - READ FROM FILE WITH HANDLE
seg000:09DB ; BX = file handle, CX = number of bytes to read
seg000:09DB ; DS:DX -> buffer
嘿嘿,它的作用就是从ds:dx开始读取3个字节。算一算:
lea dx, [bp+22Ch]
[bp+22Ch] = [082fh+22Ch] = [0A5Bh]
而:
seg000:0A5B E9 db 0E9h ; ?
seg000:0A5C 00 db 0 ;
seg000:0A5D 00 db 0 ;
所以,最后的结果是读取了E9h, 0h, 0h这三个字节,而这三个字节就是宿主的原入口的第一条指令了。
2、杀毒过程:
我们得到了宿主的原入口指令,也知道了病毒体的第一个指令的地址,事情就好办了。清除方法:恢复前三字节(E9h, 0h, 0h),然后删除后面的病毒部分。有兴趣的读者可以自行写一个这样的程序,享受学习的快乐。

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值