gyy 发表于 2008-2-1 22:44:25

纯DOS下如何截获中断

在纯DOS下,地址0~3FFh的内存区域存放这一张中断向量表,在这张表里每四个字节存放着一个中断过程的段地址和偏移地址,其中段地址存放在四个字节的高字上,偏移地址存放在四个字节的低字上。
那如何根据中断号找到自己在中断向量表里的位置呢?我们只要把中断号乘上4(字节数)后,得到的结果就是该中断在表中的位置。
比如INT 1CH中断是DOS系统每1/18.2秒执行一次的中断过程,它在中断向量表中的位置就是70H,用段+偏移地址的方式写就是0000:0070H,
那这个中断过程的段地址保存在0000:0072H和0000:0073H两个字节中
这个中断过程的偏移地址保存在0000:0070H和0000:0071H两个字节中
我们用DEBUG命令可以看到ICH中断过程在内存中的位置
输入DEBUG后,再输入D 0000:0070 L4
我们可以看到:0000:0070      53 FF 00 F0                                        S...
(在不同的机器上看到的值可能不同)
这是我们就能知道INT 1CH中断过程的地址是F000:FF53

puchonghui 发表于 2008-2-1 23:25:41

额。。。各个版本dos都一样的?还是ms dos都一样的?

gyy 发表于 2008-2-3 17:28:33

很对不起大家,前两天比较忙,没空写,今天继续

各种版本的DOS,以及现在的WINDOWS在内存中,从(物理)地址0~3FFh都是存放中断向量表的

gyy 发表于 2008-2-3 17:41:01

那我们知道INT 1CH中断过程的段地址和偏移地址与我们截获这个中断有什么关系呢?

在很久之前我以为在DOS下截获一个中断应该运行一个程序,然后靠这个程序去自动截获各种中断发出的中断请求。可是DOS是个单用户单任务的操作系统,又怎么可以在运行一个程序的时候再运行第二个程序?
后来才知道,其实答案并非我想的那样,是去运行一个程序来截获中断请求,而是把自己写的过程的地址(包括段地址和偏移地址)〖替换〗掉中断向量表中原来中断过程的地址。这样在发出中断请求的时候,DOS操作系统就会在中断向量表中找到你自己写的过程在内存中的地址,从而运行你自己的中断过程。
当我们希望执行完自己的中断过程后继续执行操作系统原来的中断的时候,这时我们就要知道原来INT 1CH中断过程在内存中的地址了。

gyy 发表于 2008-2-3 17:53:17

为什么要截获中断呢?

目的很简单,就是为了执行自己写的中断过程。
可是有些心理阴暗的人拿着自己掌握的专业知识不去做些有意义的事,技术这把双刃剑在他们手上变成了破坏的工具。
由于DOS对中断向量表没有保护,所以任何一个程序都可以随意地更改这张表,所以病毒就利用了这点(当然光靠截获中断还是不够的,还需要另一项重要的技术——内存驻留)。

以后大家都可能成为测试的精英和骨干,希望大家要有自己的职业准则和道德标准,不要滥用自己掌握的专业知识。

gyy 发表于 2008-2-3 18:04:45

什么是内存驻留?

记得以前在玩DOS游戏的时候可能会用到鼠标,所以在运行游戏之前总会运行以下MOUSE这个程序,这样在玩游戏的时候就可以使用鼠标了。
可是大家都知道DOS是个单任务的操作系统,它又是怎么同时运行两个程序的呢?
这是因为在DOS中,鼠标的中断号是33H,当我们运行MOUSE程序的时候,我们截获这个中断,使其执行我们自己写的中断过程,然后我们把自己的中断过程驻留在内存中,最后我们的MOUSE程序就能结束运行了。这样,在内存中就有了我们的中断过程,而且可以继续运行我们的游戏了。

gyy 发表于 2008-2-3 18:21:32

为了更直观地让大家对内存驻留和截获中断,编译连接下面的代码就能明白了

这段代码的作用是在当前光标位置上循环显示ASCII码从0~127的单个字符,频率为1秒中
为了实现每秒显示一个ASCII字符,所以就要截取1CH中断,1CH中断是每秒产生18.2次中断请求,这个中断发生的频率为18.2HZ,中断的周期约为55ms。

保存下面的代码,文件的扩展名必须为asm,文件主名为mytsr
然后使用MASM5.0来汇编和连接这个程序,使用的命令如下
masm mytsr.asm
回车后它会提示输入文件名,不用管它,连续3个回车就可以了
link mytsr.obj
再两个回车就可以了
连接完成后会有mytsr.exe文件,运行这个文件就可以了

运行完后,用mem /c/p可以看见我们驻留在内存里的中断过程,名字就叫mytsr

gyy 发表于 2008-2-3 18:49:45

代码

.model        tiny,stdcall
.stack

.code
        assume        ds:_text
;在标号myint与main之间的就是中断过程
myint:
        push        ax
        push        bx
        push        cx
        push        dx
        inc        cs:cycle
        cmp        cs:cycle,18        ;18.2cycle=1s
        jb        label_callsysint
        mov        cs:cycle,0
        mov        bh,0h
        mov        al,cs:ascii
        mov        cx,1
        mov        ah,0ah
        int        10h
        inc        cs:ascii
        cmp        cs:ascii,127
        jbe        label_callsysint
        mov        cs:ascii,0

label_callsysint:
        pop        dx
        pop        cx
        pop        bx
        pop        ax
        jmp        cs:
        oldintaddr        dd        ?
        cycle                dw        0
        ascii                db        0
main:
        ;获取INT 1CH中断过程的地址,bx中存放偏移地址,es中存放段地址
        push        cs
        pop        ds
        mov        ax,351ch
        int        21h
        mov        word ptr oldintaddr,bx
        mov        word ptr oldintaddr+2,es

        ;将自己的中断过程的地址写入中断向量表
        mov        ax,251ch
        mov        dx,offset myint
        int        21h

        ;退出程序,并把中断过程驻留在内存中
        mov        ax,3100h
        mov        dx,offset main
        mov        cl,4
        shr        dx,cl
        inc        dx
        add        dx,10h
        int        21h

end        main

gyy 发表于 2008-2-3 18:55:34

原本以为这个程序只能运行在纯DOS下,没想到在XP里也能运行。
页: [1]
查看完整版本: 纯DOS下如何截获中断