如何在DOS程序集中正确挂接Interrupt 28h并将其还原? [英] How do I properly hook Interrupt 28h in assembly for DOS, and restore it?

查看:109
本文介绍了如何在DOS程序集中正确挂接Interrupt 28h并将其还原?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图将Interrupt 28h的处理程序设置为我自己的例程,恢复涉及的所有寄存器和标志,并恢复原始的Interrupt处理程序. 我正在VirtualBox的DOSBox和MS-DOS 6.22下使用NASM汇编器.

I'm trying to set the handler of Interrupt 28h to my own routine, restore all the registers and flags involved, and restore the original Interrupt handler. I'm using NASM Assembler, under DOSBox and MS-DOS 6.22 in VirtualBox.

我已经考虑过调试,但是在TSR程序上进行调试听起来似乎是不可能的.我尝试将数据段推到代码段上,并保存原始数据段以供以后还原,但是即使还原了数据段后,它似乎仍挂起了计算机.

I've thought about debugging, but doing so on a TSR program sounds impossible. I've tried pushing the Data Segment onto the Code Segment, and saving the original Data Segment for restoring later, but it seems to hang the machine even after restoring the Data Segment.

section .text   ;Code Section
org 100h        ;DOS Executable Start
mov ah,35h      ;Get Interrupt Vector
mov al,28h      ;Of Interrupt 28h
int 21h         ;Call DOS Kernel
push cs         ;Push Code Segment
pop ds          ;Onto Data Segment
mov [oldseg],es ;Save Old Interrupt Vector Segment
mov [oldoff],bx ;Save Old Interrupt Vector Offset
mov ah,25h      ;Set Interrupt Vector
mov dx,resstart ;To Resstart
int 21h         ;Call DOS Kernel
mov dx,resend   ;Set Data Offset to Resend
sub dx,resstart ;Subtract Resstart
shr dx,4h       ;Shift Right 4 Bits for Paragraph
inc dx          ;One Extra Paragraph for PSP
mov ah,31h      ;Terminate and Stay Resident
xor al,al       ;Return Code
int 21h         ;Call DOS Kernel

resstart:       ;Resident Code Start
push ax         ;Save AX
push es         ;Save ES
push di         ;Save DI
push cx         ;Save CX
push ds         ;Save DS
push dx         ;Save DX
mov ah,00h      ;Set Video Mode
mov al,13h      ;To Mode 13h
int 10h         ;Call BIOS Video
mov ax,0A000h   ;VGA Segment
mov es,ax       ;Stored in ES
xor di,di       ;VGA Offset in DI
mov cx,0FA00h   ;Fill Entire Screen
mov al,09h      ;With Light Blue Color
rep stosb       ;Repeat Store AL at ES:DI
mov ah,25h      ;Set Interrupt Vector
mov al,28h      ;Of Interrupt 28h
mov ds,[oldseg] ;Restore Old Interrupt Vector Segment
mov dx,[oldoff] ;Restore Old Interrupt Vector Offset
int 21h         ;Call DOS Kernel
pop dx          ;Restore DX
pop ds          ;Restore DS
pop cx          ;Restore CX
pop di          ;Restore DI
pop es          ;Restore ES
pop ax          ;Restore AX
iret            ;Return and Restore Flags
resend:         ;Resident Code End

section .data
oldseg dw 0     ;Old Interrupt Vector Segment
oldoff dw 0     ;Old Interrupt Vector Offset

在返回原始中断向量地址并将新的中断向量地址设置为重新启动"之后,程序应终止并保持驻留状态.此后,由于DOS无需执行其他操作,Interrupt 28h将自动触发,这将反过来运行我的Interrupt处理程序.

After returning the original interrupt vector address, and setting the new interrupt vector address to "resstart", the program should terminate and stay resident. After this, Interrupt 28h would be triggered automatically since DOS has nothing else to do, which would in turn run my Interrupt handler.

Interrupt处理程序将视频模式设置为13h,尝试用浅蓝色填充整个屏幕,还原原始的Interrupt 28h处理程序,还原涉及的所有寄存器和标志,然后返回DOS.执行该程序不会产生任何结果,系统甚至不会挂起.在运行设置视频模式13h的部分并单独用蓝色填充整个屏幕时,它工作得非常好.

The Interrupt handler sets the video mode to 13h, tries to fill the entire screen with a light blue color, restores the original Interrupt 28h handler, restores all registers and flags involved, and returns to DOS. Executing this program yields no results, the system doesn't even hang. While running the part of setting video mode 13h and filling the entire screen with blue on its own separately, it works perfectly fine.

推荐答案

mov dx,resend ;Set Data Offset to Resend
sub dx,resstart ;Subtract Resstart
shr dx,4h ;Shift Right 4 Bits for Paragraph
inc dx ;One Extra Paragraph for PSP

在此.COM程序中,您正在正确保存和设置中断向量.但是,您无法准确计算出DOS.TerminateAnd StayResident函数要保留的段落数量.

In this .COM program you're saving and setting the interrupt vector correctly. However you don't calculate accurately the amount of paragraphs to keep by the DOS.TerminateAnd StayResident function.

inc dx需要向上舍入到最接近的段落.当然不考虑PSP.由于PSP具有256个字节,因此需要16个段落.

The inc dx is needed to round up to the nearest paragraph higher. Certainly not to account for the PSP. That would require 16 paragraphs since the PSP has 256 bytes.

分配给此.COM程序的内存从PSP开始,因此DX计数也必须从那里开始.

The memory that was allocated to this .COM program starts with the PSP and so the DX count must start there also.

mov     dx, resend 
shr     dx, 4
inc     dx
mov     ax, 3100h   ; DOS.TerminateAndStayResident
int     21h

提示如果将此重新发送标签与段落边界对齐,则不再需要inc dx.

Tip If you align this resend label to a paragraph boundary, the inc dx is no longer required.

如果您当前的代码在诸如virtualbox之类的仿真器中部分起作用,那是因为以前由程序占用的内存尚未被例如程序外壳.与DOS不同,仿真器可以在很远的距离执行命令解释器.

If your present code worked partially in an emulator like virtualbox it's because the memory formerly occupied by your program was not yet overwritten by e.g. the program shell. Emulators, unlike DOS, have the luxury to execute the command interpreter from a far distance.

尽管系统挂起,但使用virtualbox屏幕确实显示为蓝色

the screen does fill with blue using virtualbox, though the system hangs

如果我在写东西的过程中有人关了灯,我也会上吊!这就是您的处理程序在突然更改视频模式时会执行的操作...

I would hang too if someone switch off the lights while I'm in the middle of writing something! That's what your handler does when it suddenly changes the video mode...

对于TSR程序,我们通常跳过要保留的部分,因此一次性设置所占用的空间可以由系统回收.

For a TSR program we usually jump over the part that is to be kept resident, so the space occupied by the one-time setup can be recycled by the system.

您可以使用的另一种技巧是直接在恢复中断向量的指令中写入旧中断向量的偏移量和段.处理程序中的段寄存器不再有问题.

One more trick you can use, is to write the offset and segment of the old interrupt vector directly in the instructions that will restore the vector. No more problems with segment registers in the handler.

这是我对您的程序的重写:

This is my rewrite of your program:

    org     100h
Start:
    jmp     Setup

MyInt28:
    push    ax
    push    es
    push    di
    push    cx
    push    ds
    push    dx
    mov     ax, 0013h   ; BIOS.SetVideoMode
    int     10h
    mov     ax, 0A000h
    mov     es, ax
    xor     di, di
    mov     cx, 64000/2
    mov     ax, 0909h
    cld
    rep stosw
PatchA:
    mov     ax, 0       ; Don't change this to 'xor ax,ax'
    mov     ds, ax
PatchB:
    mov     dx, 0       ; Don't change this to 'xor dx,dx'
    mov     ax, 2528h   ; DOS.SetInterruptVector
    int     21h
    pop     dx
    pop     ds
    pop     cx
    pop     di
    pop     es
    pop     ax 
    iret

Setup:                  ; Resident part ends here.
    mov     ax, 3528h   ; DOS.GetInterruptVector
    int     21h         ; -> ES:BX
    mov     [PatchA + 1], es
    mov     [PatchB + 1], bx
    mov     dx, MyInt28
    mov     ah, 25h     ; DOS.SetInterruptVector
    int     21h
    mov     dx, (256+Setup-Start+15)/16
    mov     ax, 3100h   ; DOS.TerminateAndStayResident
    int     21h

这篇关于如何在DOS程序集中正确挂接Interrupt 28h并将其还原?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆