﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-thinktalk-随笔分类-病毒</title><link>http://www.blogjava.net/ThinkingTalking/category/26284.html</link><description>Do what I can do!</description><language>zh-cn</language><lastBuildDate>Sun, 30 Sep 2007 17:05:08 GMT</lastBuildDate><pubDate>Sun, 30 Sep 2007 17:05:08 GMT</pubDate><ttl>60</ttl><item><title>防范U盘病毒Autorun.inf的绝招</title><link>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149750.html</link><dc:creator>ThinkingTalking</dc:creator><author>ThinkingTalking</author><pubDate>Sun, 30 Sep 2007 01:04:00 GMT</pubDate><guid>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149750.html</guid><wfw:comment>http://www.blogjava.net/ThinkingTalking/comments/149750.html</wfw:comment><comments>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149750.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ThinkingTalking/comments/commentRss/149750.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ThinkingTalking/services/trackbacks/149750.html</trackback:ping><description><![CDATA[<div class="NewsContent" id="NewsContentLabel">
<p>目前，U盘病毒的情况非常严重。几乎所有带病毒的U盘,根目录里都有一个autorun.inf，右键菜单多了&#8220;自动播放&#8221;、&#8220;Open&#8221;、&#8220;Browser&#8221;等项目。由于我们习惯用双击来打开磁盘，但现在我们双击，通常不是打开U盘，而是让autorun.inf里所设的程序自动播放。只需采用鼠标右键单击打开就不会激活病毒。但若你的机子有很多人，又不能保证他们不会双击打开U盘，那就可以采用下面的方法。</p>
<p>　　招一、组策略关闭AutoRun功能(适合不熟悉<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">电脑</a>者)</p>
<p>　　如果你想一次全部禁用<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">Windows</a> XP的自动播放功能，那么请按下述步骤操作：</p>
<p>　　1、单击&#8220;开始&#8594;运行&#8221;，在&#8220;打开&#8221;框中，键入&#8220;gpedit.msc&#8221;，单击&#8220;确定&#8221;按钮，打开&#8220;组策略&#8221;窗口;</p>
<p>　　2、在左窗格的&#8220;本地计算机策略&#8221;下，展开&#8220;计算机配置&#8594;管理模板&#8594;<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">系统</a>&#8221;，然后在右窗格的&#8220;设置&#8221;标题下，双击&#8220;关闭自动播放&#8221;;</p>
<p>　　3、单击&#8220;设置&#8221;选项卡，选中&#8220;已启用&#8221;复选钮，然后在&#8220;关闭自动播放&#8221;框中单击&#8220;所有驱动器&#8221;，单击&#8220;确定&#8221;按钮，最后关闭&#8220;组策略&#8221;窗口。</p>
<p>　　招二、注册表关闭AutoRun功能</p>
<p>　　在&#8220;开始&#8221;菜单的&#8220;运行&#8221;中输入Regedit，打开注册表<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">编辑</a>器，展开到</p>
<p>　　HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Exploer 主键下，在右侧窗格中找到&#8220;NoDriveTypeAutoRun&#8221;，就是这个键决定了是否执行CDROM或<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">硬盘</a>的AutoRun功能。</p>
<p>　　双击 &#8220;NoDriveTypeAutoRun&#8221;，在默认状态下(即你没有禁止过AutoRun功能)，在弹出窗口中可以看到 &#8220;NoDriveTypeAutoRun&#8221;默认键值为95,00,00,00，附件上传了抓图。其中第一个值&#8220;95&#8221;是十六进制值，它是所有被禁止自动运 行设备的和。将&#8220;95&#8221;转为二进制就是10010101，其中每位代表一个设备，<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">Windows</a>中不同设备会用如下数值表示：</p>
<p>　　设备名称 第几位 值 设备用如下数值表示 设备名称含义</p>
<p>　　DKIVE_UNKNOWN　 　0101h 不能识别的设备类型<br />
<br />
</p>
<div class="NewsContent" id="NewsContentLabel">DRIVE_NO_ROOT_DIR 1002h 没有根目录的驱动器(Drive without root directory)
<p>&nbsp;</p>
<p>　　DRIVE_REMOVABLE　 2104h 可移动驱动器(Removable drive)</p>
<p>　　DRIVE_FIXED　 3008h 固定的驱动器(Fixed drive)</p>
<p>　　DRIVE_REMOTE 4110h <a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">网络</a>驱动器(Network drive)</p>
<p>　　DRIVE_CDROM 5020h 光驱(CD-ROM)</p>
<p>　　DRIVE_RAMDISK 6040h RAM磁盘(RAM Disk)</p>
<p>　　保留 7180h 未指定的驱动器类型</p>
<p>　　在上面所列的表中值为&#8220;0&#8221;表示设备运行，值为&#8220;1&#8221;表示该设备不运行(默认情况下，<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">Windows</a>禁止80h、10h、4h、01h这些设备自动运行，这些数值累加正好是十六进制的95h，所以NoDriveTypeAutoRun&#8221;默认键值为95,00,00,00)。</p>
<p>　招三、修改权限法</p>
<p>　　1. 点开始-&gt;运行 输入 regedit.exe 回车 2. 打开注册表<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">编辑</a>器后展开项，也是进入</p>
<p>　　[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2] 3. 右键点MountPoints2 选择权限 4. 依次点击&#8220;<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">安全</a>中的用户和组&#8221;，在下面的权限中都改成拒绝 5. 刷新一遍，此后即使U盘有病毒也不会激活，双击U盘会正常进入U盘。</p>
<p>　　招四、隐藏驱动器法(适合U盘使用频繁者，如打印工作室)</p>
<p>　　如果上面的方法都不够彻底，还有一招就是把驱动器隐藏了，而用一个批处理文件来打开U盘。</p>
<p>　　1、打开注册表<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">编辑</a>器，还是进入</p>
<p>　　HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer，新建二进制值&#8220;NoDrives&#8221;，缺省值是00000000，表示不隐藏任何驱动器。键值由4个字节组成，每个字节的每一位(bit)对应从A到Z的一个盘，当相应位为1时，&#8220;我的<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">电脑</a>&#8221;中的相应驱动器就被隐藏了。第一个字节代表从A到H的八个盘，即01为A、02为B、04为C&#8230;&#8230;依此类推，第二个字节代表I到P;第三个字节代表Q到X;第四个字节代表Y和Z。U盘的盘符是接着现有盘符往下推。若你现在已经使用了C、D、E、F，那么U盘采用G：着盘符，再插入一个U盘就用H：。此时只需将G：和H：隐藏，则插入U盘也不会在&#8220;我的<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">电脑</a>&#8221;里显示。当然，用注册表<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">编辑</a>器修改注册表操作起来较为复杂， 现在有很多专门修改注册表的软件，如<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">WINDOWS</a>优化大师中展开</p>
</div>
<div class="NewsContent" id="NewsContentLabel">&#8220;<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">系统</a>性能优化/系统<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">安全</a>优化/更多设置/选择要隐藏的驱动器&#8221;，将要隐藏的盘符前的"□"里打"&#8730;",确定即可。
<p>&nbsp;</p>
<p>　　2、在桌面新建一个文本文件，输入&#8220;start (你的盘符):&#8221;，如：&#8220;start f:\&#8221;,另存为&#8220;进入U盘.bat&#8221;，之后就通过双击该批处理文件来打开U盘，不仅可防病毒，使用也很方便。</p>
<p>　　招五、禁止创建autorun.inf(保护自己的U盘)</p>
<p>　　在根目录下建立一个文件夹，名字就叫autorun.inf。由于<a class="UBBWordLink" href="http://www.hackvip.com/" target="_blank">Windows</a>规定在同一目录中，同名的文件和文件夹不能共存，这样病毒就无法创建autorun.ini文件，即使您双击盘符也不会运行病毒。</p>
<p>&nbsp;</p>
</div>
</div>
<img src ="http://www.blogjava.net/ThinkingTalking/aggbug/149750.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ThinkingTalking/" target="_blank">ThinkingTalking</a> 2007-09-30 09:04 <a href="http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149750.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用汇编编写DOS下的内存驻留程序(1)</title><link>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149743.html</link><dc:creator>ThinkingTalking</dc:creator><author>ThinkingTalking</author><pubDate>Sun, 30 Sep 2007 00:22:00 GMT</pubDate><guid>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149743.html</guid><wfw:comment>http://www.blogjava.net/ThinkingTalking/comments/149743.html</wfw:comment><comments>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149743.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ThinkingTalking/comments/commentRss/149743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ThinkingTalking/services/trackbacks/149743.html</trackback:ping><description><![CDATA[<p>绪言 <br />
0.1 内存驻留与中断 <br />
内存驻留程序英文叫Terminate and Stay Resident Program,缩写为TSR.这些程序加载进内存,执行完后,就驻留在内存里,当满足条件时,调到前台来执行。 <br />
内存驻留程序的常用形式有: <br />
&gt;诸如Borland 的SideKick弹出式实用程序 <br />
&gt;日历系统 <br />
&gt;<a class="channel_keylink" href="http://www.3800hk.com/">网络</a><a class="channel_keylink" href="http://www.3800hk.com/">服务</a>器 <br />
&gt;通讯程序 <br />
&gt;本地的DOS扩展(如CCDOS,UCDOS等中文系统都属于这个范畴) <br />
&gt;一些可恶的人利用TSR<a class="channel_keylink" href="http://www.3800hk.com/">技术</a>制作很多可恶的病毒程序,几乎所有的病毒程序都是TSR程序. <br />
就象多任务系统调度一个进程有一个调度程序一样,在PC中从前台程序进入到一个TSR,也要有一个调度者,只是PC操作系统的调度不称为调度程序,而只称为触发机制.触发机制调度TSR执行在PC机上党称为激活一个TSR.触发机制主要有以下几种: <br />
&gt;硬件中断:党用的是键盘中断INT 9H,时钟中断INT 8H,通讯中断INT 14H,磁盘中断INT 13H等等. <br />
&gt;软件中断:党用的是键盘中断INT 16H,时钟中断INT 1CH,DOS中断INT 21H,等等. <br />
&gt;以上各种的结合. <br />
从以上的触发机制可以看出,TSR和PC机的中断系统有着密切的关系.每种激活方式实际上都是与中断有关的.常用特殊的击键序列的识别码是通过截获INT 9H和INT 16H来实现.实际上不管TSR程序的哪一个环节,都与中断有着密切的关系.因此在具体进行TSR和程序设计之前,先介绍PC中断系统.在此只作简单说明. <br />
在PC机内存的最低端(0000H开始)的1K字节中,存放着256个指针即常说的中为向量或中断矢量(Interrupt vertor)，每个中断向量都指向一个子程序，该程序称为中断处理程序（Interrup handler)．一个中断向量由四个字节组成，有一个字是中断处理程序的偏移量值，后一个字是中断处理程序的段值．256中断向量一起称为中断向量表. <br />
手式计算中断向量的首址,可通过以下的公式来求得: <br />
X号中断向量的首址=0000H:X*4 <br />
当产生一个中断时,处理器都按顺序执行以下步骤: <br />
&gt;在堆栈上压入处理器的标志(相当于指令PUSHF). <br />
&gt;在堆栈上压入当前CS和IP值(相当于指令PUSH CS和PUSH IP). <br />
&gt;关闭中断(CLI) <br />
&gt;从中断向量加载的CS和IP,执行中断处理程序. <br />
当执行完中断处理程序后,一般用IRET返回,它的作用是: <br />
&gt;从堆栈上取出保存的IP和CS(相当于指令POP CS和PUSH CS). <br />
&gt;同时恢复中断前的处理器标志(相当于指令POPF). <br />
中断有多种分类,由触发的原因和实现的性质来分,可分为硬件中断和软件中断,从操作系统分层实现来说,可以分成BIOS中断,BOS中断和用户中断. <br />
一方面,BIOS和DOS通过中断系统向用户提供一个操作系统功能界面.也就是说用户(一般来说是前台程序)的功能主要是通过调用DOS和BIOS的中断<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>来实现的,具体来说就是通过INT指令来实现的.另一方面,BIOS和DOS由中断系统所构成,BIOS对硬件成为高层的功能,并通过中断的形式向用户提供. <br />
如果在当前程序执行的同时,能将一块代码放在内存,把中断向量指向代码中的子程序,那么在当前程序执行中产生中断时,就有可能执行不属于当前程序和操作系统的代码,产生的中断可能是当前程序产生的软件中断,也可能是由硬件产生的硬件中断.这就是单任务的PC操作系统可能执行多于一个进程的简单说明. <br />
在PC中断系统中有几个中断具有周期性,即INT 8H,INT 1CH和INT 28H.它们或者周期性被执行用于时间计时,或者周期性产生用于等待.它们是在实现TSR时进行轮询触发的基础.键盘中断(INT 9H和INT 16H)当用户击键时发生,利用它们是进行热键处理的基础.串行口通讯也是触发的一个重要机制.此外众多的软件中断也是触发的媒介. </p>
<p>0.2 DOS的可重入性分析 <br />
一个多任务操作系统之所以能使多个进行并存,是因为操作系统的大部分代码是可以了重的,对于临界资源有相应的PV操作,使得当调度一个新的进程时,能完整地保存前一个里程的现场,当再一次调度被挂起的进程时能象没有被中断一样继续执行. <br />
对于PC机来说,代码的重入性比较弱,对临界资源没有PC操作.当我们用中断程序启动用户的TSR时,如果只保存标志和寄存器,以及当前进程一些信息,那么只保存了当前程序的一部分现场,DOS的临界资源不会<a class="channel_keylink" href="http://www.3800hk.com/">自动</a>保存.在进行TSR设计时,一定要了解PC操作系统的重入性和临界资源. <br />
重入性总是体现在代码上,所谓可重入代码的指这样的代码,即该代码被执行时还没有从中退出,由于某种原因又一次或者多次进入相同的代码,该代码每次的执行结果都是正确的,就说该代码是可重入的.相反,如果结果不正确,那么就就该代码是不可重入的.下面是一个可重入的子程序的例子: <br />
Add proc near <br />
cmp DS:word ptr [si],0 <br />
je DonotAddTheValue <br />
add ax,DS:word ptr [si] <br />
DonotAddTheValue: <br />
ret <br />
Add endp <br />
上面的例子不管在其中任何一处再一次执行该子程序,执行结果不变.为了说明,只举多种可能性中的一种. <br />
mov ds,0100h ;ds=0100h <br />
mov si,0010h ;si=0010h <br />
mov ax,0001h ;ax,=0001h <br />
call Add <br />
cmp 0100h:word ptr [0010h],0 ;Call Add subroutine <br />
push ds ;Interrupted <br />
push si <br />
push ax <br />
mov ds,0200h ;ds=0200h <br />
mov si,0200h ;si=0020h <br />
mov ax,0003h ;ax=0003h <br />
call Add <br />
cmp 0200h:word ptr [0020h],0 ;0200:0020h=0004h <br />
jne <br />
add ax,0200h:word ptr [0020h] ;ax=0007h <br />
ret ;Return <br />
pop ax ;ax=0001h <br />
pop si ;si=0010h <br />
pop ds ;ds=0100h <br />
iret ;Return to Add subroutine <br />
jne <br />
add ax,0100h:word ptr [0100h] ;ax= 0001h <br />
;0100h:0010h= 0002h <br />
;---------------------------------------- <br />
;ax = 0003h <br />
ret <br />
mov bx,ax <br />
而下面的子程序是不可重入的: <br />
Add proc near <br />
mov Temp,ax <br />
mov ax,DS:word ptr [si] <br />
cmp ax,0 <br />
je DonotTheValue <br />
add ax,Temp <br />
DonotTheValue: <br />
ret <br />
Temp: <br />
dw 0 <br />
Add endp <br />
可以利用检查可重入子程序的方法检查这个子程序的不可重入性,尝试一下在" mov ax,DS:word ptr [si]"指令后再次执行该子程序,那么就会出第一次调用返回的结果不对. <br />
mov ds,0100h ;ds=0100h <br />
mov si,0010h ;si=0010h <br />
mov ax,0001h ;ax,=0001h <br />
call Add <br />
mov Temp,ax ;Call Add subroutine <br />
;Temp=0001h <br />
mov ax,0100h:word ptr [0010h] ;0100h:0010h=0002h <br />
;ax=2 <br />
push ds ;Interrupted <br />
push si <br />
push ax <br />
mov ds,0200h ;ds=0200h <br />
mov si,0020h ;si=0020h <br />
mov ax,0003h ;ax=0003h <br />
call Add <br />
mov Temp,ax ;Temp=0003h <br />
mov ax,0200h:word ptr [0020h] ;0200h:0020h=0004h <br />
cmp ax,0 ;ax=0004h <br />
jne ;Not equal ,add <br />
add ax,Temp ;ax=0007h <br />
ret ;Return to the interrupted point <br />
pop ax ;ax=0002h <br />
pop si ;si=0010h <br />
pop ds ;ds=0100h <br />
iret ;Return to Add subroutine <br />
cmp ax,0 ;ax=2 <br />
jne ;No equal,add <br />
add ax,Temp ;ax =0002h <br />
;0100h:0010h =0003h <br />
;---------------------------------------- <br />
;ax =0005h <br />
ret <br />
mov bx,ax <br />
上面执行的结果是AX=5,实上正确的结果应该是AX=3,这是由于当Add子程序从中断子程序再一次被调用时,修改了Temp的值,当从中断返回时不能正确恢复其值. <br />
解决的方法是把Temp放在堆栈中,当每次Add子程序被调用时Temp的地址都不一样,因此原调用的Temp值不会被第二次在中断中调用的Add所破坏. <br />
Add proc near <br />
push bp ;Store BP <br />
sub sp,2 ;distribute a byte space in the stack <br />
mov bp,sp ;SS:BP point to the stack head <br />
temp equ SS:word ptr [BP+0] ;Explain the pointer to SS:BP <br />
mov Temp,ax <br />
mov ax,DS:word ptr [si] <br />
cmp ax,0 <br />
je DonotAddTheValue <br />
add ax,Temp <br />
DonotAddTheValue: <br />
add sp,2 ;Release the dsitributed space in the stack <br />
pop bp ;Restore BP <br />
ret <br />
Add endp <br />
对于DOS来说,DOS的内存<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>就象Temp变量,它被分配在<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>区,而不在堆栈上,因此DOS从总体上是不可重入的.从最后的一个例子看来.重入性跟堆栈有很大的关系.可重入代码允许在任何时候被中断,其所有的变量都存放在该代码的私有堆栈中.DOS是一个单任务的操作系统,在执行INT 21H的代码时是不允许中断DOS,并再次调用INT 21H的.每个时该最多有一个进程在调用DOS的代码. <br />
对DOS的重入性,以及相应所作的处理总结如下: <br />
&gt;当通过INT 21H调用DOS时,DOS会使三个内部栈之一:I/O栈,磁盘栈和辅助栈.功能00H到处0CH使用I/O栈,除了不致命错误处理程 序以外使用磁盘栈,致命错误处理程序使用辅助栈.在这种栈切换模式下,如果前台处在INT 22H中,而TSR调用了使用相同栈的DOS功能, 就会使前台程序保存栈中的<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>被TSR的<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>覆盖掉;但如果调用不同栈的DOS功能,那将是安全的.INT 21H中的几个功能调即33H,50H, 51H,62H,和64H由于非常简单,使用用户栈,因此在任何情况下都是可重入的.避免这种不可重入的简单方法是当前台程序正处在INT 21H 中时,不要调用INT 21H.或者如果前台程序正在处理INT 21H时,只允许调用不同栈的INT 21H功能. <br />
&gt;DOS<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>区中有一个InDOS标志,也探源为DOS安全标志,表示当前访问DOS功能是来否安全.由于DOS不可重入,它指示当前是 否处于DOS中,激活TSR和代码可检查该标志(34H),如果DOS忙,则不能激活使用INT 21H 调用的TSR. <br />
&gt;当前台程序执行能设置错误状态的DOS功能时,DOS会把扩展错误信息存放起来,正常情况下,前台程序可以读取扩展错误信息; 如果在前台程序读取信息之前激活TSR,且TSR也执行能报告错误信息的DOS功能,则后来的错误信息会覆盖原来的错误信息,前台程序就 会得不到正确的错误信息.因此必须在激活TSR之前保存(59H)这些错误信息,并在退出以前把它们恢复(5D0AH)成原来的值. <br />
&gt;大多硬件中断如INT 13H,INT 0BH和INT 0CH等都是不可重往返.如果设置一引起寄存器,而在此时被TSR打断,执行类似的设置 ,就会出现非常情况,端口是不会<a class="channel_keylink" href="http://www.3800hk.com/">自动</a>保持值的.在进入这些中断时设置一个进入的标志,如果TSR检查到标志已置,则不调用相应的中断. <br />
&gt;最好也不要重入INT 10H,INT 25H,和INT 26H中断.在进入这些中断时设置一个进入的标志,如果TSR检查到标志已置,则不调用 相应的中断. <br />
&gt;最好能接管INT 1BH,INT 23H和INT 24H中断. <br />
&gt;保存DOS的<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>交换区(SDA)可以安全地使用的DOS的功能.SDA保存了DOS几乎所有内部<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>,如果保存(5D06H)和恢复(5D0BH)SDA ,DOS就变成在任何时候都可以重入的了.当DOS处在关键区中时,调用INT 2AH.一旦处在关键区中,就不能改变SDA.在关键区的结束处会 调用INT21H的81H和82H功能. <br />
0.3 内存驻留程序设计一般过程 <br />
驻留程序分成两个部分,即暂驻部分和驻留部分.驻留程序要完成安装检测,激活和删除等过程. <br />
基本上可抽象成以下几个过程: <br />
&gt;取中断向量 <br />
&gt;保存旧的中断向量 <br />
&gt;设置或恢复中断向量 <br />
&gt;中断处理程序的链接 <br />
&gt;检测是来呀已驻留 <br />
&gt;执行终止并驻留 <br />
&gt;TSR的删除 <br />
删除TSR比较复杂,必须按下列步骤进行: <br />
&gt;检查中断向量是否已经被替换.如果没有替换,就恢复所有的中断向量;如果某个中断向量被替换,则跳过下面各步,不能删除该 TSR. <br />
&gt;TSR的PSP中偏移量16H存放着父进程的PSP.把这个值改为当前进程的地址. <br />
&gt;把当前PSP设为TSR的PSP <br />
&gt;执行INT 21H的4CH功能,释放TSR占用的内存,关闭所有文件,并使用PSP中存放的父进程地址和终止地址. <br />
&gt;这里控制返回到初始进程中,当前PSP也指向初始进程,所有寄存器值包括SS和SP都不确定. <br />
在执行完上述步骤后,要恢复寄存器. <br />
如果要无条件地删除TSR,必须监控每个TSR对中断向量表,内存控制块和设备驱动程序链的修改. <br />
0.5 缩写语表 <br />
ASCIZ: 以零结束的ASCII字符串. <br />
BPD: "BIOS Parameter Block (BIOS 参数块)"的缩写.含有对驱动器的低级参数的说明. <br />
CDS: "Current Directory Structure(当前目录结构)"的缩写,含有某个逻辑驱动器的当前目录,类型和其它信息. <br />
DPB: "DOS Drive Parameter Block(DOS驱动器参数块)"的缩写,含有某个逻辑驱动器的介质说明及一些内部信息. <br />
DPL: "DOS Parameter List (DOS参数表)"的缩写,该<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>结构用来传递参数给SHARE和<a class="channel_keylink" href="http://www.3800hk.com/">网络</a>功能调用. <br />
DTA: "Disk Transfer Address(磁盘传输地址)"的缩写,指示对磁盘进行<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>读写的功能调用不必显式地给出缓冲区地址. <br />
FAT: "File Allocation Table(文件分配表)"的缩写,磁盘的文件分配表记录了所使用的簇信息. <br />
FCB: "File Control Block(文件控制块)"的缩写,在DOS的1.X版本中,用FCB来记录文件打开的状态.. <br />
IFS: "Installable File System(可安装的文件系统)"的缩写,它允许一个非DOS格式的介质被DOS所使用. 大多数情况下IFS 与<a class="channel_keylink" href="http://www.3800hk.com/">网络</a>驱动器非常相似,尽管IFS最典型的情况是一个本地驱动器而不是一个远程驱动器. <br />
JFT: "Job File Table(工作文件表)或Open File Table(打开文件表)"的缩写,程序PSP中的JFT可用来将文件句柄转换成SFT值. <br />
NCB: "Network control Block(<a class="channel_keylink" href="http://www.3800hk.com/">网络</a>控制块)"的缩写.NCB可用传递对NETBIOS的请求和接受来自NETBIOS处理程序的状态信息. <br />
PSP: "Porgram Segment Prefix(程序段前缀)"的缩写.当程序被装入时,PSP为一个预留的256字节的<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>区它包含了程序调用时 的命令行内容和一些DOS的内部信息. <br />
SDA: "DOS Swappable Data Area (DOS对换<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>区)"的缩写.SDA中包含有DOS内部使用的记录某个正在处理的功能调用状态的 所有变量. <br />
SFT: "System File Table(系统文件表)"的缩写,SFT是一个DOS内部<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>结构,在DOS 2+版本的</p>
句柄功能调用中用于管理某个已打 开文件的状态,这就和在DOS1.X中,FCB管理已打开文件状态一样. 
<img src ="http://www.blogjava.net/ThinkingTalking/aggbug/149743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ThinkingTalking/" target="_blank">ThinkingTalking</a> 2007-09-30 08:22 <a href="http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用汇编编写DOS下的内存驻留程序(2)</title><link>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149742.html</link><dc:creator>ThinkingTalking</dc:creator><author>ThinkingTalking</author><pubDate>Sun, 30 Sep 2007 00:21:00 GMT</pubDate><guid>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149742.html</guid><wfw:comment>http://www.blogjava.net/ThinkingTalking/comments/149742.html</wfw:comment><comments>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149742.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ThinkingTalking/comments/commentRss/149742.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ThinkingTalking/services/trackbacks/149742.html</trackback:ping><description><![CDATA[基本原理 <br />
2.1 8086/8088 <br />
IBM PC中央处理单元(Central Processing Unit)是微处理器Inter 8088,8088是8086是小的版本.对于编写程序而言,两者几乎完全相同.两者之间的差别是在于:它们对外的沟通.8086和外界沟通时是经由16位的输入输出通道,内存存取也是每次以16位为单位,8088和8086极为相似,但是它和外界沟通时就必须经由16位的通道. <br />
2.1.1 寄存器 <br />
8086/8088的结构简单,其中包含了一组一般用途的16位寄存器.AX,BX,CX,DX,BP,SI,DI.其中AX,BX,CX,DX还可以分成8位的寄存 器,譬如:AX可分为AH,AL;BX可分为BH,BL;CX可分为CH,CL;DX可分为DH,DL.寄存器BP,SI,DI的用途也没有特别的限制,但是却不能分成两个字节.另外寄存器SP主要是用来当做堆栈指针.除此之外,还有四个非常重要的段寄存器(Segment Register):CS,DS,SS,ES.指令指针(Instru -ction pointer)IP是用来控制目前CPU执行到哪一个指令. <br />
8086设计时考虑到要和8位的CPU8080兼容.8位的计算机是使用两个字节(亦即16位)来定址,因此其定址空间可以达64K字节.16位的CPU在地址设定上选择了完全不同的方法.CPU以段(Segment)为单位,每一段范围内包括64K字节,而内存中则可以包含许多段.所以,操作系统可以在一个段内执行.而使用者的程序则可以在另一个段内执行.在一个段内,程序包可以把计算机视为只有64K字节内存空间.因此原先8位计算机上执行的程序就可以很容易地移植到16位计算机上.除此之外,内存段也可以彼此重叠,因而两个不同的程序就可以共用某一块内存.段值是以寄存器来设定的,而实际的地址值则是把段值(16位)往左移4位,然后再加上16位的位移(Offset),因此构成20位的地址值.所以8086可以直接做20位的地址,也就是可能存取到一兆字节的内存.在这一兆字节的内存中,IBM PC保留了最前面的320K字节给系统的ROM BIOS和显示内存,因此使用者最多也就能使用640K字节. <br />
2.1.2 寻址方式 <br />
寻址方式(Addressing mode)是一台计算机上许多复杂操作的关键所在.8086提供了以下几种寻址方法:立即寻址,内存间接寻址, 寄存器间接寻址等. <br />
立即寻址,直接使用数字. <br />
内存间接寻址,数值存放在<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>段中的某个位置. <br />
mov bx,foo <br />
foo dw 5 <br />
寄存器间接寻址.有两种寄存器可以使用在这种寻址方式下:基址寄存器(Base Register)和索引寄存器(Index Register).基址寄存 器分别是BX和BP,索引寄存器则是SI和DI.在这种寻址方式下,寄存器存放了<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>段中的地址值. <br />
mov ax,0F000h <br />
mov es,ax <br />
mov si,0FFFEh <br />
mov dl,byte ptr es:[si] <br />
上面的程序使用间接寻址方式,由寄存器SI读出位于F000:FFFE位置的<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>.寄存器间接存取时,最多只能使用玛个基址寄存器各 一个索引寄存器. <br />
以上的寻址方式可以做不同的结合,因此组合后的结果很多. <br />
2.1.3 标志 <br />
8086有9个一位的标志(Flag),它们可以用指示CPU的各种状态.以下是9个标志的简介: <br />
CF(Carry Flag):CF为1时就表示算术运算的结果超出正确的长度. <br />
PF(Parity Flag):PF为1就表示使用偶校验,PF为0就表示使用奇校验. <br />
AF(Auxiliary Carry Flag):和CF相同,只是它使用在低4位的结果.AF通常都使用在20位的地址计算上. <br />
ZF(Zero Flag):ZF为1就表示运算结果是0,否则ZF就为0. <br />
SF(Sign Flag):SF为1就表示运算结果的最高位是1,否则SF就为0. <br />
TF(Trap Flag):TF为1,CPU就单步地执行,在这种模式下每完成一个指令就发生一个特殊的中断. <br />
IF(Interrupt Enable Flag):IF为1,允许CPU接收外界的中断,否则IF就为0. <br />
DF(Direction Flag):这个标志使用在循环指令,譬如:MOVS,MOVSB,MOVSW,CMPS,CMPSB和CMPSW.如果DF为1,循环运行时就使地 址值往前增加.如果DF为0,则使地址往后减少. <br />
OF(Over Flag):OF为1,表示一个考虑正负号的运算超出了正确的字节的长度. <br />
2.1.4 循环 <br />
所有的循环指令都是以CX作为计数器.一个循环会反复地执行直到CX等于某一特定值为止.以下的程序就是利用反复地相加,完成 两个数的相乘. <br />
mov ax,0 <br />
mov cx,4 <br />
next: add ax,6 <br />
loop next <br />
在上面的程序中,LOOP指令执行时会把CX减1,并且检查CX的内容;如果CX等于0,就转移到下一条指令,否则就跳到NEXT标示的地方 执行. <br />
也可以用下面的程序完成相同的功能: <br />
mov ax,0 <br />
mov cx,4 <br />
next: <br />
add ax,6 <br />
dec cx <br />
cmp cx,0 <br />
jne next <br />
2.1.5 内存的<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>结构 <br />
8088是以字节为存取<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>的基本单位.计算机的存储结构是8位的字节,但是CPU本身处理<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>则是以16位为单位.在内存中,都遵 循一个原则,即:高高低低的存储方式.高字节对应高地址,低字节对应低地址. <br />
下面是一个简单程序,在AX中放入一个字节的内容并显示: <br />
cseg segment <br />
org 100h <br />
assume cs:cseg,ds:cseg <br />
start: <br />
mov bx,cs <br />
mov ds,bx <br />
mov ah,'H' <br />
mov al,'L' <br />
mov test,ax <br />
mov al,[si] ;First byte of test <br />
call dchar <br />
mov al,[si+1] ;Second byte of test <br />
call dchar <br />
ret <br />
;Display the character contained in AL <br />
dchar proc <br />
push ax <br />
push bx <br />
mov bh,1 <br />
mov ah,0eh <br />
int 10h <br />
pop bx <br />
pop ax <br />
ret <br />
dchar endp <br />
test dw ? <br />
cseg ends <br />
end start
<img src ="http://www.blogjava.net/ThinkingTalking/aggbug/149742.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ThinkingTalking/" target="_blank">ThinkingTalking</a> 2007-09-30 08:21 <a href="http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149742.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用汇编编写DOS下的内存驻留程序(3)</title><link>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149741.html</link><dc:creator>ThinkingTalking</dc:creator><author>ThinkingTalking</author><pubDate>Sun, 30 Sep 2007 00:20:00 GMT</pubDate><guid>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149741.html</guid><wfw:comment>http://www.blogjava.net/ThinkingTalking/comments/149741.html</wfw:comment><comments>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149741.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ThinkingTalking/comments/commentRss/149741.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ThinkingTalking/services/trackbacks/149741.html</trackback:ping><description><![CDATA[三 中断矢量 <br />
3.1 IBM PC提供的中断 <br />
IBM PC有两种基本形态的中断.如果是由外围设备所产生的中断就叫做硬件中断(Hardware interrupt),譬如:键盘,磁盘机和时钟等外围设备都可以产生硬件中断.外围设备所产生的中断信号都连接到中断控制器,中断控制器可以根据它们之间的重要性来安排优先顺序,以便使CPU有效地处理这些硬件信号.另一种中断是软件中断(Software interrupt),软件中断也叫做陷井(Trap),它是由执行中的软件所产生.虽然软件包中断的处理方式和硬件中断完全相同,但是通常软件中断是希望执行操作系统所提供的<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>. <br />
表3.1是IBM PC所提供的中断,这些中断是根据中断号码和中断矢量(Interrupt vector)排列. <br />
IBM PC的用户或是编写应用程序的程序人员很少会直接接触到硬件中断,除非是使用某些特殊的硬件,或是需要较严格的要求时,最常被修改的硬件中断是敲键盘所产生的中断(9H),尤其是文本编辑的程序.大体而言,只有硬件设计者基是系统程序人员才会注意到所有在硬件中断;编写内存驻留程序的设计人员则只使用到部分硬件中断而已,尤其是:键盘中断和计时器(Timer)的中断. <br />
反之,软件中断对于任何编写汇<a class="channel_keylink" href="http://www.3800hk.com/">编程</a>序的人,甚至对编写高级语言程序的人都相当的重要.软件中断是应用程序进入到IBM PC操作系统的接口,经由这些接口应用程序才可以执行所要求的系统<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>. <br />
其中软件中断中最重要,同时也是最常被汇编语言程序设计师所用到是DOS INT 21H.这个中断是执行DOS系统调用的软件中断,它可以让应用程序执行任何DOS的操作. <br />
接下来最有用的软件中断是ROM-BIOS(基本输入输出系统)所提供的中断.这些软件中断是IBM PC所提供的的低层次<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>,譬如:键盘输入,显示器输出和磁盘机的输入与输出等. <br />
3.2 键盘输入的方法 <br />
以下就以IBM PC从键盘读取字符为例子,来说明中断的工作方式.IBM PC从键盘读取字符时,使用了两种不同形式中断,亦即:硬件中断和软件中断.当使用者从键盘敲下一个键时,键盘的线路就会送出一个信号.这个信号会造成硬件中断发生,从而触发低层次的键盘中断处理程序开始执行.这个中断处理程序马上从键盘的硬件读取使用者所敲入的字符,然后把它放到一个队列中,如果这个队列填满时,键盘中断处理程序会使IBM PC发出一声响.键盘中断处理程序做完这些事情之后,它就把控制权交还给原先被中断的程序.如果有一个程序希望从键盘读取一个字符时,它就发出适当的软件中断信号,这时候就由相对应的中断处理程序去检查键盘队列,并且传回队列中的第一个字符. <br />
上面所介绍的键盘输入工作方式,在中断驱动系统中很普遍地采用.这和做法可以把实际上需要输入的应用程序和实际上执行输入的处理部分分开来.这种做法也可以用在其它不同形式的输入和输出外围设备. <br />
3.3 改变输入矢量 <br />
中断矢量储存在IBM PC最前面的400H个字节中.每一个矢量的长度是四个字节组成,这四个字节内所存放的是中断处理程序执行的地址值.其中前两个字节包含地址值的位移(Offset)部分,后面的两个字节则包含了段(Segment)部分. <br />
中断矢量有两种修改方法.可以直接地设置中断矢量的地址值,或是使用DOS所提供的系统调用设置中断矢量的地址值. <br />
3.3.1 直接设置中断矢量 <br />
因为中断矢量只是存放地址值的存储位置,因此我们可以直接地把地址存放到存储位置中.以下是一个小例子: <br />
mov ax,0 <br />
mov es,ax <br />
mov word ptr es:24,offset Keyboard <br />
mov word ptr es:26,seg Keyboard <br />
在许多情况下,上面的程序都可以正确地执行.但是如果上面的程序正在执行时突然敲下一个键的话,就可能会问题;而最糟的情 况是发生:第三个MOV已经执行完毕,而第四个MOV尚未执行时.如果在此时敲下任何键的话,键盘中断矢量都没有任何意义,而造成整个系 统死机.因此我们可以在设置中断矢量时,让中断无效,譬如: <br />
mov ax,0 <br />
mov es,ax <br />
cli <br />
mov word ptr es:24,offset Keyboard <br />
mov word ptr es:26,seg Keyboard <br />
上面的做法在大部分的情况下都可以正确地执行.但是CLI这个指令无法停止NMI中断(不可屏蔽中断),因此如果发生NMI中断时就 没用办法.下面的这一种做法虽然比较复杂,但是对于所有的中断都有效,这包括了NMI中断在内: <br />
mov word ptr kbd-ptr[0],offset Keyboard <br />
mov word ptr kbd-ptr[2],seg Keyboard <br />
mov di,0 ;Use Di to Set ES to zero <br />
mov es,di ;Set ES to destination segment <br />
mov di,24 ;Set DI to destination offset <br />
mov si,offset kbdptr ;set SI to source offset <br />
mov cx,2 ;Set word count to 2 <br />
cld ;Set direction to forward <br />
cli ;Disable interrupts <br />
rep movsw ;Copy the new vector <br />
sti ;Enable interrupts <br />
kbdptr dd ? <br />
上面的程序中,kbdptr是两个字节(WORD)的指针(Pointer),其中包含了键盘 中断处理程序的起始志趣值.REP这个指令将根据寄存 器CX所设置的次数来重复执行MOVSW,而整个指令就如同单一的指令一样.NMI中断不能够发生在一个完整的指令中.因为地址值搬移的操 作都能包含在一个单一指令中,因此可以免除任何中断的干扰. <br />
3.3.2 使用DOS来设置中断矢量 <br />
因为要想安全地设置中断矢量需要一些技巧,因此DOS提供了一项特殊的<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>,以帮助程序人员安全地设置中断矢量,如果只使用 DOS所提供的这项<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>来设定中断矢量的话,那么就不必担心会发生前面所叙述的差错.DOS同时也提供了:读取中断矢量的<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>.因为读 取中断矢量的内容不会修改系统的状态;因此若直接写程序读取,也很安全.但是如果你要自己直接读取中断矢量的内容时,就必须计算 出中断矢量的位置.而DOS已经提供了这项<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>. <br />
使用DOS所提供的系统调用,来读取中断矢量的内容时,必须利用INT 21H中的函数35H(读取中断矢量),这个函数热气矢量号码来 计算中断矢量的地址,然后返回其中的内容.以下就是一个例子: <br />
Old_Keyboard_IO dd ? <br />
mov al,16h <br />
mov ah,35h <br />
int 21h <br />
mov word ptr Old_Keyboard_IO,bx ;Offset of interrupt handler <br />
mov word ptr Old_Keyboard_IO,es ;Segment of interrupt handler <br />
用DOS来设置中断矢量例子: <br />
New_Keyboard_IO dd ? <br />
mov word ptr New_Keyboard_IO,bx ;Offset of interrupt handler <br />
mov word ptr New_Keyboard_IO,es ;Segment of interrupt handler <br />
mov al,16h <br />
mov ah,25h <br />
int 21h <br />
3.4 检查中断矢量 <br />
这里都是采用COM格式<a class="channel_keylink" href="http://www.3800hk.com/">编程</a>,可以建立一个BAT文件来处理写好的程序,以减少击键次数.设BAT文件名为MAKE.BAT: <br />
MASM %1 <br />
LINK %1 <br />
EXE2BIN %1.EXE %1.COM <br />
如果写好的程序名为MACRO.ASM,则可敲入: <br />
C:\MAKE MACRO.ASM <br />
即可. <br />
3.5 显示中断矢量 <br />
下面这个例子可以列出所有的重要的中断矢量内容,在刚刚打开PC时,并且没有执行任何驻留程序时,可以发现所有的中断矢量段值都相同,这些地址值所存放的是ROM的程序.当你修改中断矢量之后,就可以利用这个程序观察到中断矢量的变化.以下就是IVEC.ASM的内容: <br />
cseg segment para public 'CODE' <br />
org 100h <br />
jmp start <br />
assume cs:cseg,ds:cseg <br />
start: <br />
mov bx,cs ;Make data seg be the same as <br />
mov ds,bx ;the code seg <br />
call vectors <br />
waitIn: <br />
mov ah,0bh <br />
int 21h <br />
cmp al,0ffh <br />
jne waitIn <br />
mov ah,4ch <br />
int 21h <br />
;**************************************************************************** <br />
;Scan through display table,prinying two vectors per line <br />
;If any record has an interrupt #=zero,this indicates <br />
;end of the table. <br />
;**************************************************************************** <br />
mov di,offset disptab ;Pointer to start of table <br />
mov dh,0 ;Zero out top half of DX <br />
vloop: <br />
mov dl,[di] ;Get the interrupt number <br />
cmp dl,0 ;If it's zero,we are done <br />
je vdone ;so exit loop <br />
add di,1 ;Advance pointer 1 byte <br />
mov si,[di] ;Get pointer to description <br />
call dvector ;Call the display routine <br />
add di,2 ;Get the interrupt number <br />
mov dl,[di] ;Advance to the next record <br />
cmp dl,0 ;If it's zero,we are done <br />
je vdone ;so exit loop <br />
add di,1 ;Advance pointer 1 byte <br />
mov si,[di] ;get pointer to description <br />
call dvector ;Call the display routine <br />
add di,2 ;Advance to the next record <br />
jmp vloop <br />
vdone: ;Print final CRLF <br />
ret <br />
vectors endp <br />
;---------------------------------------------------------------------------- <br />
;Displays an interrupt vector.Display is in the form of <br />
;&lt;banner&gt;,&lt;interrupt#&gt;,&lt;seg&gt;:&lt;offset&gt; <br />
;where &lt;interrupt #&gt;,&lt;seg&gt;and&lt;offset&gt; <br />
;are all dexadecimal numbers <br />
;Call with <br />
;DX -interrupt number <br />
;DS:SI -pointer to banner string <br />
;---------------------------------------------------------------------------- <br />
dvector proc near <br />
call dstring ;Display the string in DS:SI <br />
call dbyte ;Display the byte in DL <br />
call dspace ;Display a space <br />
call dspace <br />
; <br />
mov al,dl ;move the interrupt number to AL <br />
mov ah,35h ;Function is Get interrupt vector <br />
int 21h <br />
mov dx,bx ;Move BX to DX so we can display <br />
call ddword ;double-word in ES:DX <br />
call dEndFra <br />
call dcrlf ;Display a newline <br />
ret <br />
dvector endp <br />
;---------------------------------------------------------------------------- <br />
;DS:SI points to ASCII string to be printed <br />
;---------------------------------------------------------------------------- <br />
dstring proc near <br />
push si <br />
push ax <br />
dis: mov al,[si] ;Fetch the next character <br />
cmp al,0 ;If it's zero,we are done <br />
je disdone <br />
call dchar ;If not,point it <br />
inc si ;Advance pointer to nest char <br />
jmp dis <br />
disdone:pop ax <br />
pop si <br />
ret <br />
dstring endp <br />
;---------------------------------------------------------------------------- <br />
;ES:DX contains double word to be displayed <br />
;---------------------------------------------------------------------------- <br />
ddword proc near <br />
push dx ;Save offset temporarily <br />
mov dx,es ;Move segment to DX <br />
call dsword ;Display segment <br />
call dcolon ;Print a ";" <br />
; call dcrlf <br />
pop dx ;Restore offset to DX <br />
call dsword ;Display offset <br />
ret <br />
ddword endp <br />
;---------------------------------------------------------------------------- <br />
;DX containes single word to be displayed <br />
;---------------------------------------------------------------------------- <br />
dsword proc near <br />
push dx ;Save low byte temporarily <br />
mov dl,dh ;Move high byte to low byte <br />
call dbyte ;Display high byte <br />
pop dx ;Restore low byte to DL <br />
call dbyte ;Display low byte <br />
ret <br />
dsword endp <br />
;---------------------------------------------------------------------------- <br />
;DL contains byte to be displayed <br />
;---------------------------------------------------------------------------- <br />
dbyte proc near <br />
push ax ;Save any registers used <br />
push dx <br />
push si <br />
push dx ;Save low nybble temporarily <br />
push cx ;Save CX <br />
mov cl,4 ;Set shift count to 4 <br />
shr dx,cl ;Shift high nybble into low nybble <br />
and dx,0fh ;Mask out all but low nybble <br />
mov si,dx ;Use low nybble as index into <br />
mov al,hextab[si] ;hexadecimal character table <br />
call dchar ;Display character <br />
pop cx ;Restore CX <br />
pop dx ;Restore low nybble <br />
and dx,0fh ;Mask out all but low nybble <br />
mov si,dx ;Use low nybble as an index into <br />
mov al,hextab[si] ;hexadecimal character table <br />
call dchar ;Display character <br />
pop si ;Restore registers <br />
pop dx <br />
pop ax <br />
ret <br />
dbyte endp <br />
;---------------------------------------------------------------------------- <br />
;Display a ":" <br />
;---------------------------------------------------------------------------- <br />
dcolon proc near <br />
mov al,':' <br />
call dchar <br />
ret <br />
dcolon endp <br />
;---------------------------------------------------------------------------- <br />
;Display a " " <br />
;--<br />
-------------------------------------------------------------------------- <br />
dspace proc near <br />
mov al,' ' <br />
call dchar <br />
ret <br />
dspace endp <br />
;---------------------------------------------------------------------------- <br />
;Display a Carriage Return/Line Feed <br />
;---------------------------------------------------------------------------- <br />
dcrlf proc near <br />
mov al,0dh <br />
call dchar <br />
mov al,0ah <br />
call dchar <br />
ret <br />
dcrlf endp <br />
;---------------------------------------------------------------------------- <br />
;Display the character contained in AL <br />
;---------------------------------------------------------------------------- <br />
dchar proc near <br />
push ax <br />
push bx <br />
mov bh,1 <br />
mov ah,0eh <br />
int 10h <br />
pop bx <br />
pop ax <br />
ret <br />
dchar endp <br />
;---------------------------------------------------------------------------- <br />
;Data define <br />
;---------------------------------------------------------------------------- <br />
hextab db '0123456789ABCDEF',0 <br />
disptab db 05h ;Print screen <br />
dw v05 <br />
db 19h ;Bootstrap loader <br />
dw v19 <br />
db 08h ;Timer tick <br />
dw v08 <br />
db 1ah ;Real_time clock <br />
dw v1a <br />
db 09h ;Keyboard input <br />
dw v09 <br />
db 1bh ;CTRL_Break handler <br />
dw v1b <br />
db 0bh ;Comm.port 1 <br />
dw v0b <br />
db 1ch ;Timer control <br />
dw v1c <br />
db 0ch ;Comm.port 0 <br />
dw v0c <br />
db 1dh ;Pointer to video parameter table <br />
dw v1d <br />
db 0dh ;Hard disk controller <br />
dw v0d <br />
db 1eh ;Pointer to disk parameter table <br />
dw v1e <br />
db 0eh ;Floppy disk controller <br />
dw v0e <br />
db 1fh ;Pointer graphics character table <br />
dw v1f <br />
db 0fh ;Printer controller <br />
dw v0f <br />
db 20h ;Program terminate <br />
dw v20 <br />
db 10h ;Video driver <br />
dw v10 <br />
db 21h ;DOS universal function <br />
dw v21 <br />
db 11h ;Equipment check <br />
dw v11 <br />
db 22h ;Pointer to termination handler <br />
dw v22 <br />
db 12h ;Memorey size check <br />
dw v12 <br />
db 23h ;Pointer to Ctrl_C handler <br />
dw v23 <br />
db 13h ;Disk driver <br />
dw v13 <br />
db 24h ;Pointer to critical error handler <br />
dw v24 <br />
db 14h ;Communications driver <br />
dw v14 <br />
db 25h ;Absolute disk read <br />
dw v25 <br />
db 15h ;Cassette driver <br />
dw v15 <br />
db 26h ;Absolute disk write <br />
dw v26 <br />
db 16h ;Keyboard driver <br />
dw v16 <br />
db 27h ;Terminate and stay resident <br />
dw v27 <br />
db 17h ;Printer driver <br />
dw v17 <br />
db 2fh ;Print spooler <br />
dw v2f <br />
db 18h ;Rom basic <br />
dw v18 <br />
db 0 <br />
dw 0 <br />
v05 db 186,5 dup (20h),'Print screen:',26 dup (20h),0 <br />
v08 db 186,5 dup (20h),'Timer tick controller:',17 dup (20h),0 <br />
v09 db 186,5 dup (20h),'Keyboard input:',24 dup (20h),0 <br />
v0b db 186,5 dup (20h),'Communication port 1:',18 dup (20h),0 <br />
v0c db 186,5 dup (20h),'Communication port 0:',18 dup (20h),0 <br />
v0d db 186,5 dup (20h),'Hard disk controller:',18 dup (20h),0 <br />
v0e db 186,5 dup (20h),'Floppy disk controller:',16 dup (20h),0 <br />
v0f db 186,5 dup (20h),'Printer controller:',20 dup (20h),0 <br />
v10 db 186,5 dup (20h),'Video driver:',26 dup (20h),0 <br />
v11 db 186,5 dup (20h),'Equipment check:',23 dup (20h),0 <br />
v12 db 186,5 dup (20h),'Memory size check:',21 dup (20h),0 <br />
v13 db 186,5 dup (20h),'Disk driver:',27 dup (20h),0 <br />
v14 db 186,5 dup (20h),'Communication driver:',18 dup (20h),0 <br />
v15 db 186,5 dup (20h),'Cassette driver:',23 dup (20h),0 <br />
v16 db 186,5 dup (20h),'Keyboard driver:',23 dup (20h),0 <br />
v17 db 186,5 dup (20h),'Printer driver:',24 dup (20h),0 <br />
v18 db 186,5 dup (20h),'ROM BASIC:',29 dup (20h),0 <br />
v19 db 186,5 dup (20h),'Bootstrap loader:',22 dup (20h),0 <br />
v1a db 186,5 dup (20h),'Real_time clock:',23 dup (20h),0 <br />
v1b db 186,5 dup (20h),'Ctrl_break handler:',20 dup (20h),0 <br />
v1c db 186,5 dup (20h),'Timer control:',25 dup (20h),0 <br />
v1d db 186,5 dup (20h),'Video parameter table:',17 dup (20h),0 <br />
v1e db 186,5 dup (20h),'Disk parameter:',24 dup (20h),0 <br />
v1f db 186,5 dup (20h),'Graphic character table:',15 dup (20h),0 <br />
v20 db 186,5 dup (20h),'Programe terminate:',20 dup (20h),0 <br />
v21 db 186,5 dup (20h),'DOS universal function:',16 dup (20h),0 <br />
v22 db 186,5 dup (20h),'Terminate vector:',22 dup (20h),0 <br />
v23 db 186,5 dup (20h),'Ctrl_C vector:',25 dup (20h),0 <br />
v24 db 186,5 dup (20h),'Critical error vector:',17 dup (20h),0 <br />
v25 db 186,5 dup (20h),'Absolute disk read:',20 dup (20h),0 <br />
v26 db 186,5 dup (20h),'Absolute disk write:',19 dup (20h),0 <br />
v27 db 186,5 dup (20h),'Terminate and stay resident:',11 dup (20h),0 <br />
v2f db 186,5 dup (20h),'Print spooler:',25 dup (20h),0 <br />
cseg ends <br />
end start
<img src ="http://www.blogjava.net/ThinkingTalking/aggbug/149741.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ThinkingTalking/" target="_blank">ThinkingTalking</a> 2007-09-30 08:20 <a href="http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149741.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用汇编编写DOS下的内存驻留程序(4)</title><link>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149740.html</link><dc:creator>ThinkingTalking</dc:creator><author>ThinkingTalking</author><pubDate>Sun, 30 Sep 2007 00:19:00 GMT</pubDate><guid>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149740.html</guid><wfw:comment>http://www.blogjava.net/ThinkingTalking/comments/149740.html</wfw:comment><comments>http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149740.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ThinkingTalking/comments/commentRss/149740.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ThinkingTalking/services/trackbacks/149740.html</trackback:ping><description><![CDATA[四&nbsp;基本的驻留程序<br />
4.1&nbsp;一个基本的COM程序<br />
&nbsp;DOS之下有两种形式的可执行文件,这两种文件分别是COM文件和EXE文件.其中,COM文件可以迅速地加载和执行,但是其大小不能超过64K字节,只能有一个段,代码段.而且起始地址为100H指令必须为程序的启动指令.EXE文件可以加载到许多个段中,因此程序的大小没有限制,但是程序加载的过程就比较慢,而且对于内存驻留程序来说还会造成更大的麻烦.<br />
&nbsp;以下是一个可以正确执行的COM文件,但其内容是空的;只是一个COM文件的框架,可以把你写的任何应用部分加在这个文件中,形成一个COM格式的内存驻留程序:<br />
&nbsp;&nbsp;;Section&nbsp;1<br />
&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;org&nbsp;100h<br />
&nbsp;&nbsp;;Section&nbsp;2<br />
&nbsp;&nbsp;start:&nbsp;<br />
&nbsp;&nbsp;&nbsp;ret<br />
&nbsp;&nbsp;;Section &nbsp;3<br />
&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;&nbsp;&nbsp;end&nbsp;start<br />
&nbsp;上面的程序可以分成三部分,第一部分定义了代码段和<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>段分别放在程序中的位置,以及执行代码的起始地址.第二部分是可执行的程序,在这个例子只一个RET指令而已.第三部分是程序包段的终结,其中END叙述包含了程序开始执行地址.<br />
&nbsp;若是把上面的程序经过汇编连接,你会发现所产生的COM文件只有一个字节长.这是因为所产生的COM文件没有程序段前缀(Programsegmetn&nbsp; profix),因为在DOS下所有和COM文件都有相同的程序段前缀.当DOS加载一个COM文件到内存中时,就会<a class="channel_keylink" href="http://www.3800hk.com/">自动</a>地产生一份正确的程序段前缀.一个程序在执行的过程中,可以根据需要修改其程序段前缀,但是在一开始,所有COM文件的程序前缀都是相同的.下面是程序前缀的格式.<br />
&nbsp;&nbsp;&nbsp;&nbsp;偏移位置&nbsp;&nbsp;&nbsp;&nbsp;含义<br />
&nbsp;&nbsp;&nbsp;&nbsp;0000H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;程序终止处理子程序地址(INT 20H)<br />
&nbsp;&nbsp;&nbsp;&nbsp;0002H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;分配段的结束地址,段值<br />
&nbsp;&nbsp;&nbsp;&nbsp;0004H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;保留<br />
&nbsp;&nbsp;&nbsp;&nbsp;0005H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;调用DOS的<a class="channel_keylink" href="http://www.3800hk.com/">服务</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;000AH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;前一个父程序的IP和CS<br />
&nbsp;&nbsp;&nbsp;&nbsp;000EH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;前一个父程序的CONTROL_C处理子程序地址<br />
&nbsp;&nbsp;&nbsp;&nbsp;0012H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;前一个父程序包的硬件错误处理子程序地址<br />
&nbsp;&nbsp;&nbsp;&nbsp;0016H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;保留<br />
&nbsp;&nbsp;&nbsp;&nbsp;002CH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;环境段的地址值<br />
&nbsp;&nbsp;&nbsp;&nbsp;005EH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;保留<br />
&nbsp;&nbsp;&nbsp;&nbsp;005CH&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;FCB1<br />
&nbsp;&nbsp;&nbsp;&nbsp;006CH&nbsp;`&nbsp;&nbsp;&nbsp;&nbsp;FCB2<br />
&nbsp;&nbsp;&nbsp;&nbsp;0080H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;命令行的参数和磁盘转移区域<br />
4.2&nbsp;一个最小的内存驻留程序<br />
&nbsp;上面的程序只是一个一般的DOS程序而已.并不是内存驻留的.以下是一个基本的内存驻留程序结构:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg;ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org&nbsp;100h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start:&nbsp;&nbsp;&nbsp;&nbsp;;Section 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;done:&nbsp;&nbsp;&nbsp;&nbsp;;Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset done<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;27h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 4<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;start<br />
&nbsp;和前一个程序相比,这个程序只是增加了一个DONE部分.这个部分使用了INT 27H这个中断调用,来终止并驻留在内存(Terminate and Stay Resident)中.使用INT 27H这个中断调用时,必须设定好一个指针,让这个指针指向内存中可以使用的部分,事实上,这就相当于设置一个COM文件可加载的位置.另外DOS还提供了INT 21H,AH=31H(驻留程序,Keep process),但是使用这个中断调用时,我们必须设定所保留的内存大小,而不是设定一个指针;另外这个中断调用会送出退出码.<br />
&nbsp;使用INT 27H时,必须设定一个指针指向可用存储位置的开头,以便让DOS用来加载稍后执行的程序.DOS本身有一个指针,这个指针是加载COM文件或EXE文件时的基准地址值.INT尿27H 会改变这个指针或为新的数值.同时造成新指针和旧指针之间的存储空间无法让DOS使用因此这样做会造成可用存储位置愈来愈少.<br />
&nbsp;调用INT 27H时所使用的指针是个FAR指针,其中DX存放的是位移指针(Offset pointer),它可以指到64K字节之内的范围.而DOS是段指针(Segment pointer),它可以指到IBM PC中640K字节的任何一个段.在上面的例子中,DS的内容不必另外设定,因为当COM文件加载时,DS的内容就CS的内容相同了.<br />
&nbsp;经常在编写汇<a class="channel_keylink" href="http://www.3800hk.com/">编程</a>序时,常犯的一个错误就是:把assume ds:cseg这个叙述误认为是,存放某一预设值到DS中,事实上,汇编语言程序中的Assume叙述不会产生任何的程序代码,这个功能是告诉汇编器做某些必要的假设,以便正确地汇<a class="channel_keylink" href="http://www.3800hk.com/">编程</a>序.譬如以下的程序:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.............<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ah,radix<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.............<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;radix&nbsp;db&nbsp;16<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.............<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;上面的程序汇编时,当汇编器看到mov ah,radix这个指令时,它就根据assume ds:cseg来产生一定形式的赋值指令.在面的Assume ds:cseg叙述是告诉汇编器,<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>段就位于目前的代码段中.这是内存驻留程序的一项重要关键.如果DS的内容和CS不相同时,无论是否有assume 叙述,程序执行时都会失败.<br />
4.3&nbsp;改良的内存驻留程序<br />
&nbsp;上面所介绍的内存驻留程序实际上没有做任何事,只是驻留在内存中而已.事实上,在START和END之间放入任何程序代码,都只会执行一次而已然后就永远驻留在内存中,除非是使用转移指令转到START的地址去,否则将永远无法被使用.还要注意一点,START的地址值并非固定不变,它会根据程序执行时计算机的状态而改变.<br />
&nbsp;下面的这个程序只是把需要驻留的程序代码装载好,但是并不会执行.<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org &nbsp;100h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp&nbsp;initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;app_start:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;initialize:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 4<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int &nbsp;27h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 5<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;start<br />
&nbsp;上面的程序一开始执行时就传到initialize标志的地方,装置好驻留在内存的应用部分.原先的DONE已经改成initialize,而驻留在内存的程序代码则放在App_Start 和Initialize之间.<br />
&nbsp;另外,你也许注意到了,程序的起始地址并不是Initialize而是Start.这是因为所有COM程序的起始地址都是100H;而上面的程序中Start是放在100H的地方.如果把Initialize放在End之后,Initialize就变成起始地址,但是这样的程序无法透过EXE2BIN转换成COM文件了.如果无法产生COM文件时,那么就必须直接处理段的内容.<br />
4.4&nbsp;减少内存的额外负担<br />
&nbsp;到目前为止,都没有接触到程序前缀,当使用INT 27H时,事实上是把指针以前的东西都保留在内存中,这也包括了COM的程序段前缀.因为COM文件执行完毕后,才可以把程序段前缀移掉. <br />
&nbsp;从上面的事实可以看出:如果程序段前缀只能在COM装置程序结束后才可以移去,那么就可以由驻留在内存中的程序代码完成.要做到这一点,可以把整个程序往下移动256个字节.但又如何做到这一点呢?我们可以设定一个标志(Flag),用来指示这个程序是否执行过.如果这个驻留程序或是第一次执行时,就把整个程序往下移动256个字节,以便把程序段前缀移去.但是如果驻留程序在装置好之后,经过一段长时间仍然没有被执行时,怎么办呢?如果同时载入了好几个驻留程序时,双该如何呢?这些重要的事情都需要使用不同的程序代码来解决.如果说这些程序代码超出了256字节时,那么所占用的存储位置就超出程序段前缀所浪费的空间.有些人用一些比较简短的代码来解决这个问题,但是还是比较麻烦.因此对于大部分的内存驻留程序而言,除非存储空间太少,以至于256字节变得很重要,否则最好不要去处理程序段前缀,这样子会让你的程序简洁而且容易阅读.<br />
4.5&nbsp;使用驻留程序<br />
&nbsp;上面介绍了如何把程序加载到内存,并且让它永远留在内存中,接下来,介绍如何来使用驻留在内存中的程序.<br />
&nbsp;内存驻留程序的使用方法和它原先的设计有密切的关系.譬如,截获键盘输入的程序就必须通过键盘输入的软件中断,或是敲键盘所产生的硬件中断来使用.其它的驻留程序可能就必须靠:系统时钟,系统调用,或是其它的中断才有办法使用.这些驻留程序必须要和以上的使用方法连结;而且在驻留程序安装好之后,至少必须建立一种使用的管道,否则驻留程序将无法使用.<br />
&nbsp;IBM PC必须经由事件来驱动,譬如:键盘,系统时钟,或是软件中断.这些事件可以被截获,然后根据所发生的事件来执行一定的动作.因此必须让中断事件发生时,先执行我们的程序,而非系统的程序.<br />
&nbsp;譬如,当我们设计一个截获键盘输入的驻留程序时,就必须把驻留程序和执行键盘输入的系统调用连结起来.当DOS或是应用程序希望从键盘读取一个字符时,它就必须执行INT 16H调用.因此如果我们能够在调用INT 16H时,先执行我们的驻留程序,那么驻留程序就可能变成应用程序和操作系统间的桥梁.<br />
&nbsp;可以使用INT 21H中断调用中AH=25H来完成以上的要求.设置中断矢量可以更改INT 16H原先的中断矢量内容,让它改为指向我们的程序.譬如以下的例子所示:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org &nbsp;100h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp&nbsp;Initialize&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_keyboard_io&nbsp;proc&nbsp;far<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sti<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iret<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_keyboard_io&nbsp;endp&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initialize:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset new_keyboard_io<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;al,16h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ah,25h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;21h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset Initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;27h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;start<br />
&nbsp;上面的程序和4.3的程序结构是一样的,但是仍然有一些重要的改变.在Section 1和Section 2.在Section 1把驻留部分修改成子程序形式(Procedure),这样做是为了增加程序的可读性.另外,驻留部分多加了两个指令,STI和IRET.其中STI是设置中断标志(Set Interrupt Flag)和起始中断(Enable interrupts).<br />
&nbsp;当CPU发生中断时,它就关闭中断标志,因此CPU就不再接受中断.事实上,CPU会专心地为目前发生的中断<a class="channel_keylink" href="http://www.3800hk.com/">服务</a>.当CPU停止接受中断时,任何硬件中断的信号都会被忽略,譬如:键盘,时钟脉冲,磁盘机信号,调制解调器的中断.如果CPU一直不接受中断,那么就会漏掉一些重要的信息,计算机系统也可能因此而死机.因此虽然CPU可以停止接受中断一段时间,但是却不能够久.<br />
&nbsp;第二个重要的指令是IRET,从中断返回(Return from interrupt).IRET的功能和RET极相似,RET是用来从被调用 的子程序中返回,而IRET则是用来从中断程序返回.但是使用IRET返回时,它会从堆栈中先取出返回的地址值,然后再取出CPU的状态标志(State Flag).CPU的状态标志在CPU接受中断时,会<a class="channel_keylink" href="http://www.3800hk.com/">自动</a>地推入堆栈中.因此执行IRET指令后,CPU的状态就恢复成未中断前的状态;也就是说CPU就可以继续接受外界的中断(CPU状态标志中断包括了中断标志).严格地说,STI和IRET在这个例子中都是多余的,但是对于实际的中断处理程序而言,这两个指令都很重要.<br />
&nbsp;另外,使用设置中断矢量的中断调用时,暂存器AL必须存入所要设置的中断矢量,而中断矢量指针则必须放到暂存器DS:DX中.<br />
4.6&nbsp;连接中断处理程序<br />
&nbsp;若是把前一节的程序拿来执行时,键盘是无法输入的,事实上,处理键盘的硬件中断处理程序会继续地读取敲入的字符,并且放到等待队列中,直到队列填满为止;但是由于读取等待队列的软件中断INT 16H已经被改变了,因此队列的内容就永远取不出来.<br />
&nbsp;现在写一个中断处理程序,这个中断处理程序只是调用原先的键盘中断处理程序,一旦做到这一点之后,接下来就可以根据键盘的输入做修改.以下就是调用原先键盘处理程序的驻留程序:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org &nbsp;100h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp&nbsp;Initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Old_Keyboard_IO&nbsp;dd&nbsp;?&nbsp;<br />
;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_keyboard_io&nbsp;proc&nbsp;far<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sti&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pushf<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;ds:nothing<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;call&nbsp;&nbsp;Old_Keyboard_IO<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;nop&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iret<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_keyboard_io&nbsp;endp&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initialize:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;bx,cs<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ds,bx<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;al,16h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ah,35h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;21h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;word ptr Old_Keyboard_IO,bx<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;word ptr Old_Keyboard_IO[2],es<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;End Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset new_keyboard_io<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;al,16h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ah,25h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;21h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset Initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;27h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;start<br />
&nbsp;上面的程序中,第一部分是两个字(Double word),这是用来存放旧的键盘中断矢量.因为COM的程序都只限制在一个段中,因此<a class="channel_keylink" href="http://www.3800hk.com/">数据</a>段和代码段都在同一段中.而原先的中断处理程序和我们所编写的中断处理程序未必会在同一段中,所以必须使用双字来储存地址值.<br />
&nbsp;双字Old_Keyboard_IO可以放在驻留程序中的任何地方;但是一般来说,放在Jmp Initialize 之后会比较方便;因为如果必须使用DEBUG来检查程序的话,可以比较容易调试.<br />
&nbsp;上面程序中的第二部分是驻留程序的主体,其中包括了一个调用原先键盘中断处理程序的模拟中断.因为原先的键盘中断处理程序必须使用INT的方式调用,而不是使用CALL的指令调用;因此必须先使用PUSHF把CPU状态标志压入堆栈中,然后配合上CALL来模拟INT的动作.<br />
&nbsp;注意一点,assume ds:nothing这一行是汇编指示,而不是程序代码.它是用来告诉汇编器在产生下一行机器码时,不要更会目前DS的内容;这样做才可以让汇编器为下一个指令产生双字的地址值.<br />
&nbsp;当Call Old_Keyboard_IO指令执行时,控制权就转移到旧的键盘中断处理程序.而当这个中断调用执行完时,它就执行IRET指令,于是控制权又交还到目前的驻留程序.这样做,不但可以让原先的键盘中断程序包为我们工作,同时也可以掌握控制权.如果只使用IMP指令,跳到旧的键盘中断处理程序包去,而不把CPU状态标志推入堆栈中,那么一旦执行到IRET时,就真正返回到中断的状态.<br />
&nbsp;上面程序中的第三部分是启动代码部分,在这一部分中,设定好新的中断矢量,同时把旧的中断矢量存放在驻留程序代码中,以便让驻留程序使用.<br />
4.7&nbsp;检查驻留程序<br />
&nbsp;到目前为止,已经成功地把驻留程序加在应用程序和DOS的键盘输入之间;接下来可以修改输入的字符.在这一节中,我们准备截获键盘的输入,并且把"Y"改成"y","y"改成"Y".<br />
&nbsp;以下是程序代码:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;segment<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;org &nbsp;100h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;start:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp&nbsp;Initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Old_Keyboard_IO&nbsp;dd&nbsp;?&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_keyboard_io&nbsp;proc&nbsp;far<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sti&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;ah,0<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;je&nbsp;ki0<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;ds:nothing<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp&nbsp;&nbsp;Old_Keyboard_IO<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 2<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ki0:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pushf<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume &nbsp;ds:nothing<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;call&nbsp;&nbsp;Old_Keyboard_IO<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;al,'y'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jne&nbsp;&nbsp;ki1<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;al,'y'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jmp&nbsp;&nbsp;kidone<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ki1:&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cmp&nbsp;&nbsp;al,'Y'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;jne&nbsp;&nbsp;kidone<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;&nbsp;al,'y'<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;kidone:&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iret<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;new_keyboard_io&nbsp;endp&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Initialize:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;assume&nbsp;cs:cseg,ds:cseg<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;bx,cs<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ds,bx<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;al,16h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ah,35h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;21h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;word ptr Old_Keyboard_IO,bx<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;word ptr Old_Keyboard_IO[2],es<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;;End Section 3<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset new_keyboard_io<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;al,16h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;ah,25h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;21h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;mov&nbsp;dx,offset Initialize<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;27h<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;cseg&nbsp;ends<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;end&nbsp;start<br />
&nbsp;在面的程序第一部分主要是检查AH是否等于0(读取字符).如果AH不等于0,就用旧的中断处理程序来处理其它的功能:1H(读取键盘状态),2H(读取键盘标志).在这里,使用JMP指令,而非使用CALL来模拟软件中断;因此原先的中断处理程序结束后,就直接返回到中断前的状态.<br />
&nbsp;程序的第二部分是处理AH=0H时的情形.首先程序中断模拟一个软件中断来调用旧的键盘处理程序,是为了在读完字符之后,控制权能交还到我们的驻留程序,接下来的几行程序是检查读到的字符是不是"Y"和"y",如果是的话就修改它.<br />
&nbsp;可以借执行这个程序,来验证其是否正确.除此之外,也可以证明,在操作系统和应用程序之间可以加入一层控制码.这一层控制码可以先选择性地加强或取代某些DOS的功能,修改结果以满足我们的要求.
 <img src ="http://www.blogjava.net/ThinkingTalking/aggbug/149740.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ThinkingTalking/" target="_blank">ThinkingTalking</a> 2007-09-30 08:19 <a href="http://www.blogjava.net/ThinkingTalking/archive/2007/09/30/149740.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>