程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 匯編語言 >> 匯編語言編寫DOS下的內存駐留程序(1)

匯編語言編寫DOS下的內存駐留程序(1)

編輯:匯編語言

緒言
0.1 內存駐留與中斷
 內存駐留程序英文叫Terminate and Stay Resident Program,縮寫為TSR.這些程序加載進內存,執行完後,就駐留在內存裡,當滿足條件時,調到前台來執行。
 內存駐留程序的常用形式有:
  >諸如Borland 的SideKick彈出式實用程序
  >日歷系統
  >網絡服務器
  >通訊程序
  >本地的DOS擴展(如CCDOS,UCDOS等中文系統都屬於這個范疇)
  >一些可惡的人利用TSR技術制作很多可惡的病毒程序,幾乎所有的病毒程序都是TSR程序.
 就象多任務系統調度一個進程有一個調度程序一樣,在PC中從前台程序進入到一個TSR,也要有一個調度者,只是PC操作系統的調度不稱為調度程序,而只稱為觸發機制.觸發機制調度TSR執行在PC機上黨稱為激活一個TSR.觸發機制主要有以下幾種:
  >硬件中斷:黨用的是鍵盤中斷INT 9H,時鐘中斷INT 8H,通訊中斷INT 14H,磁盤中斷INT 13H等等.
  >軟件中斷:黨用的是鍵盤中斷INT 16H,時鐘中斷INT 1CH,DOS中斷INT 21H,等等.
  >以上各種的結合.
 從以上的觸發機制可以看出,TSR和PC機的中斷系統有著密切的關系.每種激活方式實際上都是與中斷有關的.常用特殊的擊鍵序列的識別碼是通過截獲INT 9H和INT 16H來實現.實際上不管TSR程序的哪一個環節,都與中斷有著密切的關系.因此在具體進行TSR和程序設計之前,先介紹PC中斷系統.在此只作簡單說明.
 在PC機內存的最低端(0000H開始)的1K字節中,存放著256個指針即常說的中為向量或中斷矢量(Interrupt vertor),每個中斷向量都指向一個子程序,該程序稱為中斷處理程序(Interrup handler).一個中斷向量由四個字節組成,有一個字是中斷處理程序的偏移量值,後一個字是中斷處理程序的段值.256中斷向量一起稱為中斷向量表.
 手式計算中斷向量的首址,可通過以下的公式來求得:
  X號中斷向量的首址=0000H:X*4
 當產生一個中斷時,處理器都按順序執行以下步驟:
  >在堆棧上壓入處理器的標志(相當於指令PUSHF).
  >在堆棧上壓入當前CS和IP值(相當於指令PUSH CS和PUSH IP).
  >關閉中斷(CLI)
  >從中斷向量加載的CS和IP,執行中斷處理程序.
 當執行完中斷處理程序後,一般用IRET返回,它的作用是:
  >從堆棧上取出保存的IP和CS(相當於指令POP CS和PUSH CS).
  >同時恢復中斷前的處理器標志(相當於指令POPF).
 中斷有多種分類,由觸發的原因和實現的性質來分,可分為硬件中斷和軟件中斷,從操作系統分層實現來說,可以分成BIOS中斷,BOS中斷和用戶中斷.
 一方面,BIOS和DOS通過中斷系統向用戶提供一個操作系統功能界面.也就是說用戶(一般來說是前台程序)的功能主要是通過調用DOS和BIOS的中斷服務來實現的,具體來說就是通過INT指令來實現的.另一方面,BIOS和DOS由中斷系統所構成,BIOS對硬件成為高層的功能,並通過中斷的形式向用戶提供.
 如果在當前程序執行的同時,能將一塊代碼放在內存,把中斷向量指向代碼中的子程序,那麼在當前程序執行中產生中斷時,就有可能執行不屬於當前程序和操作系統的代碼,產生的中斷可能是當前程序產生的軟件中斷,也可能是由硬件產生的硬件中斷.這就是單任務的PC操作系統可能執行多於一個進程的簡單說明.
 在PC中斷系統中有幾個中斷具有周期性,即INT 8H,INT 1CH和INT 28H.它們或者周期性被執行用於時間計時,或者周期性產生用於等待.它們是在實現TSR時進行輪詢觸發的基礎.鍵盤中斷(INT 9H和INT 16H)當用戶擊鍵時發生,利用它們是進行熱鍵處理的基礎.串行口通訊也是觸發的一個重要機制.此外眾多的軟件中斷也是觸發的媒介.

0.2 DOS的可重入性分析
 一個多任務操作系統之所以能使多個進行並存,是因為操作系統的大部分代碼是可以了重的,對於臨界資源有相應的PV操作,使得當調度一個新的進程時,能完整地保存前一個裡程的現場,當再一次調度被掛起的進程時能象沒有被中斷一樣繼續執行.
 對於PC機來說,代碼的重入性比較弱,對臨界資源沒有PC操作.當我們用中斷程序啟動用戶的TSR時,如果只保存標志和寄存器,以及當前進程一些信息,那麼只保存了當前程序的一部分現場,DOS的臨界資源不會自動保存.在進行TSR設計時,一定要了解PC操作系統的重入性和臨界資源.
 重入性總是體現在代碼上,所謂可重入代碼的指這樣的代碼,即該代碼被執行時還沒有從中退出,由於某種原因又一次或者多次進入相同的代碼,該代碼每次的執行結果都是正確的,就說該代碼是可重入的.相反,如果結果不正確,那麼就就該代碼是不可重入的.下面是一個可重入的子程序的例子:
  Add proc near
   cmp DS:word ptr [si],0
   je DonotAddTheValue
   add ax,DS:word ptr [si]
  DonotAddTheValue:
   ret
  Add endp
 上面的例子不管在其中任何一處再一次執行該子程序,執行結果不變.為了說明,只舉多種可能性中的一種.
  mov ds,0100h    ;ds=0100h
  mov si,0010h    ;si=0010h
  mov ax,0001h    ;ax,=0001h
  call Add
  cmp 0100h:word ptr [0010h],0  ;Call Add subroutine
  push ds     ;Interrupted
  push si
  push ax
  mov ds,0200h    ;ds=0200h
  mov si,0200h    ;si=0020h
  mov ax,0003h    ;ax=0003h
  call Add
  cmp 0200h:word ptr [0020h],0  ;0200:0020h=0004h
  jne
  add ax,0200h:word ptr [0020h]  ;ax=0007h
  ret      ;Return
  pop ax     ;ax=0001h
  pop si     ;si=0010h
  pop ds     ;ds=0100h
  iret      ;Return to Add subroutine


  jne
  add ax,0100h:word ptr [0100h]  ;ax=   0001h
        ;0100h:0010h= 0002h
        ;----------------------------------------
        ;ax  = 0003h
  ret
  mov bx,ax
 而下面的子程序是不可重入的:
  Add proc near
   mov Temp,ax
   mov ax,DS:word ptr [si]
   cmp ax,0
   je DonotTheValue
   add ax,Temp
  DonotTheValue:
   ret
  Temp:
   dw  0
  Add endp
 可以利用檢查可重入子程序的方法檢查這個子程序的不可重入性,嘗試一下在" mov ax,DS:word ptr [si]"指令後再次執行該子程序,那麼就會出第一次調用返回的結果不對.
  mov ds,0100h    ;ds=0100h
  mov si,0010h    ;si=0010h
  mov ax,0001h    ;ax,=0001h
  call Add
  mov Temp,ax    ;Call Add subroutine
        ;Temp=0001h
  mov ax,0100h:word ptr [0010h]  ;0100h:0010h=0002h
        ;ax=2
  push ds     ;Interrupted
  push si
  push ax
  mov ds,0200h    ;ds=0200h
  mov si,0020h    ;si=0020h
  mov ax,0003h    ;ax=0003h
  call Add
  mov Temp,ax    ;Temp=0003h
  mov ax,0200h:word ptr [0020h]  ;0200h:0020h=0004h
  cmp ax,0     ;ax=0004h
  jne      ;Not equal ,add
  add ax,Temp    ;ax=0007h
  ret      ;Return to the interrupted point
  pop ax     ;ax=0002h
  pop si     ;si=0010h
  pop ds     ;ds=0100h
  iret      ;Return to Add subroutine
  cmp ax,0     ;ax=2
  jne      ;No equal,add
  add ax,Temp    ;ax   =0002h
        ;0100h:0010h =0003h
        ;----------------------------------------
        ;ax   =0005h
  ret     
  mov bx,ax
 上面執行的結果是AX=5,實上正確的結果應該是AX=3,這是由於當Add子程序從中斷子程序再一次被調用時,修改了Temp的值,當從中斷返回時不能正確恢復其值.
 解決的方法是把Temp放在堆棧中,當每次Add子程序被調用時Temp的地址都不一樣,因此原調用的Temp值不會被第二次在中斷中調用的Add所破壞.
  Add proc near
   push bp    ;Store BP
   sub sp,2    ;distribute a byte space in the stack
   mov bp,sp    ;SS:BP point to the stack head
  temp equ SS:word ptr [BP+0]  ;Explain the pointer to SS:BP
   mov Temp,ax
   mov ax,DS:word ptr [si]
   cmp ax,0
   je DonotAddTheValue
   add ax,Temp
  DonotAddTheValue:
   add sp,2    ;Release the dsitributed space in the stack
   pop bp    ;Restore BP
   ret
  Add endp
 對於DOS來說,DOS的內存數據就象Temp變量,它被分配在數據區,而不在堆棧上,因此DOS從總體上是不可重入的.從最後的一個例子看來.重入性跟堆棧有很大的關系.可重入代碼允許在任何時候被中斷,其所有的變量都存放在該代碼的私有堆棧中.DOS是一個單任務的操作系統,在執行INT 21H的代碼時是不允許中斷DOS,並再次調用INT 21H的.每個時該最多有一個進程在調用DOS的代碼.
 對DOS的重入性,以及相應所作的處理總結如下:
  >當通過INT 21H調用DOS時,DOS會使三個內部棧之一:I/O棧,磁盤棧和輔助棧.功能00H到處0CH使用I/O棧,除了不致命錯誤處理程 序以外使用磁盤棧,致命錯誤處理程序使用輔助棧.在這種棧切換模式下,如果前台處在INT 22H中,而TSR調用了使用相同棧的DOS功能, 就會使前台程序保存棧中的數據被TSR的數據覆蓋掉;但如果調用不同棧的DOS功能,那將是安全的.INT 21H中的幾個功能調即33H,50H,  51H,62H,和64H由於非常簡單,使用用戶棧,因此在任何情況下都是可重入的.避免這種不可重入的簡單方法是當前台程序正處在INT 21H 中時,不要調用INT 21H.或者如果前台程序正在處理INT 21H時,只允許調用不同棧的INT 21H功能.
  >DOS數據區中有一個InDOS標志,也探源為DOS安全標志,表示當前訪問DOS功能是來否安全.由於DOS不可重入,它指示當前是 否處於DOS中,激活TSR和代碼可檢查該標志(34H),如果DOS忙,則不能激活使用INT 21H 調用的TSR.
  >當前台程序執行能設置錯誤狀態的DOS功能時,DOS會把擴展錯誤信息存放起來,正常情況下,前台程序可以讀取擴展錯誤信息; 如果在前台程序讀取信息之前激活TSR,且TSR也執行能報告錯誤信息的DOS功能,則後來的錯誤信息會覆蓋原來的錯誤信息,前台程序就 會得不到正確的錯誤信息.因此必須在激活TSR之前保存(59H)這些錯誤信息,並在退出以前把它們恢復(5D0AH)成原來的值.


  >大多硬件中斷如INT 13H,INT 0BH和INT 0CH等都是不可重往返.如果設置一引起寄存器,而在此時被TSR打斷,執行類似的設置 ,就會出現非常情況,端口是不會自動保持值的.在進入這些中斷時設置一個進入的標志,如果TSR檢查到標志已置,則不調用相應的中斷.
  >最好也不要重入INT 10H,INT 25H,和INT 26H中斷.在進入這些中斷時設置一個進入的標志,如果TSR檢查到標志已置,則不調用 相應的中斷.
  >最好能接管INT 1BH,INT 23H和INT 24H中斷.
  >保存DOS的數據交換區(SDA)可以安全地使用的DOS的功能.SDA保存了DOS幾乎所有內部數據,如果保存(5D06H)和恢復(5D0BH)SDA ,DOS就變成在任何時候都可以重入的了.當DOS處在關鍵區中時,調用INT 2AH.一旦處在關鍵區中,就不能改變SDA.在關鍵區的結束處會 調用INT21H的81H和82H功能.
0.3 內存駐留程序設計一般過程
 駐留程序分成兩個部分,即暫駐部分和駐留部分.駐留程序要完成安裝檢測,激活和刪除等過程.
 基本上可抽象成以下幾個過程:
  >取中斷向量
  >保存舊的中斷向量
  >設置或恢復中斷向量
  >中斷處理程序的鏈接
  >檢測是來呀已駐留
  >執行終止並駐留
  >TSR的刪除
 刪除TSR比較復雜,必須按下列步驟進行:
  >檢查中斷向量是否已經被替換.如果沒有替換,就恢復所有的中斷向量;如果某個中斷向量被替換,則跳過下面各步,不能刪除該 TSR.
  >TSR的PSP中偏移量16H存放著父進程的PSP.把這個值改為當前進程的地址.
  >把當前PSP設為TSR的PSP
  >執行INT 21H的4CH功能,釋放TSR占用的內存,關閉所有文件,並使用PSP中存放的父進程地址和終止地址.
  >這裡控制返回到初始進程中,當前PSP也指向初始進程,所有寄存器值包括SS和SP都不確定.
 在執行完上述步驟後,要恢復寄存器.
 如果要無條件地刪除TSR,必須監控每個TSR對中斷向量表,內存控制塊和設備驅動程序鏈的修改.
0.5 縮寫語表
 ASCIZ: 以零結束的ASCII字符串.
 BPD:  "BIOS Parameter Block (BIOS 參數塊)"的縮寫.含有對驅動器的低級參數的說明.
 CDS:  "Current Directory Structure(當前目錄結構)"的縮寫,含有某個邏輯驅動器的當前目錄,類型和其它信息.
 DPB:  "DOS Drive Parameter Block(DOS驅動器參數塊)"的縮寫,含有某個邏輯驅動器的介質說明及一些內部信息.
 DPL:  "DOS Parameter List (DOS參數表)"的縮寫,該數據結構用來傳遞參數給SHARE和網絡功能調用.
 DTA:  "Disk Transfer Address(磁盤傳輸地址)"的縮寫,指示對磁盤進行數據讀寫的功能調用不必顯式地給出緩沖區地址.
 FAT:  "File Allocation Table(文件分配表)"的縮寫,磁盤的文件分配表記錄了所使用的簇信息.
 FCB:  "File Control Block(文件控制塊)"的縮寫,在DOS的1.X版本中,用FCB來記錄文件打開的狀態..
 IFS:  "Installable File System(可安裝的文件系統)"的縮寫,它允許一個非DOS格式的介質被DOS所使用. 大多數情況下IFS     與網絡驅動器非常相似,盡管IFS最典型的情況是一個本地驅動器而不是一個遠程驅動器.
 JFT:  "Job File Table(工作文件表)或Open File Table(打開文件表)"的縮寫,程序PSP中的JFT可用來將文件句柄轉換成SFT值.
 NCB:  "Network control Block(網絡控制塊)"的縮寫.NCB可用傳遞對NETBIOS的請求和接受來自NETBIOS處理程序的狀態信息.
 PSP:  "Porgram Segment Prefix(程序段前綴)"的縮寫.當程序被裝入時,PSP為一個預留的256字節的數據區它包含了程序調用時   的命令行內容和一些DOS的內部信息.
 SDA:  "DOS Swappable Data Area (DOS對換數據區)"的縮寫.SDA中包含有DOS內部使用的記錄某個正在處理的功能調用狀態的   所有變量.
 SFT:  "System File Table(系統文件表)"的縮寫,SFT是一個DOS內部數據結構,在DOS 2+版本的句柄功能調用中用於管理某個已打   開文件的狀態,這就和在DOS1.X中,FCB管理已打開文件狀態一樣.

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved