do0程序的主要任务是显示字符串,程序如下/strong>
do0: 设置 ds:si 指向字符串 mov ax,0b800h mov es,ax mov di,12*160+36*2 ;设置 es:di 指向显存空间的中间位置 mov cx,9 ;设置 cx 为字符串长度 s: mov al,[si] mov es:[di],al inc si add di,2 loop s mov ax,4c00h int 21h do0end: nop
程序写好了,可要显示的字符串放在那里呢?我们看下面的程序
assume cs:code data segment db "overflow" data ends code segment start: mov ax,cs mov ds,ax mov si,offset do0 mov ax,0 mov es,ax mov di,200h mov cx,offset do0end-offset do0 cld rep movsb 设置中断向量表 mov ax,4c00h in 21h do0: mov ax,data mov ds,ax mov si,0 mov ax,0b800h mov es,ax mov di,12*160+36*2 mov cx,9 s: mov al,[si] mov es:[di],al inc si add di,2 loop s do0end:nop end start
上面的程序,看似合理,可实际上却大错特错。注意,"overflow !”在程序12.2的data段中。程序12.2执行完成后返回,它所占用的内存空间被系统释放,而在其中存放的“overflow!”也将很可能被别的信息覆盖。而do0程序被放到了0:200处,随时都会因发生了除法溢出而被CPU执行,很难保证do0程序从原来程序12.2所处的空间中取得的是要显示的字符串“overflow! "。
因为do0程序随时可能被执行,而它要用到字符串“overflow! ",所以该字符串也应该存放在一段不会被覆盖的空间中。正确的程序如下。
assume cs:code data segment db "overflow" data ends start: mov ax,cs mov ds,ax mov si,offset do0 ;设置ds:si指向源地址 mov ax,0 mov es,ax mov di,200h ;设置es:di指向目的地址 mov cx,offset do0end-offset do0 ;设置cx的传输长度 cld ;设置传输方向为正 rep movsb 设置中断向量表 mov ax,4c00h in 21h do0:jmp short do0start db"overflow!" do0start: mov ax,data mov ds,ax mov si,202h ;设置 ds:si 指向字符串 mov ax,0b800h mov es,ax mov di,12*160+36*2 ;设置 es:di 指向显存空间的中间位置 mov cx,9 ;设置 cx 为字符串长度 s: mov al,[si] mov es:[di],al inc si add di,2 loop s mov ax,4c00h int 21h do0end:nop code ends end start
在程序中,将“overflow!”放到do0程序中,程序12.3执行时,将标号do0到标号do0end之间的内容送到0000:0200处。 注意,因为在do0程序开始处的“overflow!”不是可以执行的代码,所以在" overflow !”之前加上一条jmp指令,转移到正式的do0程序。当除法溢出发生时,CPU执行0:200处的jmp指令,跳过后面的字符串,转到正式的do0程序执行。 do0程序执行过程中必须要找到“overflow! ",那么它在哪里呢?首先来看段地址,"overflow!”和do0的代码处于同一个段中,而除法溢出发生时,CS中必然存放do0的段地址,也就是“overflow!”的段地址;再来看偏移地址,0:200处的指令为jmp shortdo0start,这条指令占两个字节,所以“overflow!”的偏移地址为202h