在一个论坛里看到一个贴子,题目要求如下:
------------------------------------
求一个tsr汇编的源码,整点运行一次批处理文件
需要在一个纯dos环境中隔一段时间运行一次批处理文件,因为系统需要一直运行另一个程序,
想来想去还是用汇编做一个整点调用批处理文件的com程序比较适合.
------------------------------------
这个要求,老手是不屑,新手是不懂,却最适合刚啃完一本汇编书的中手.
我们来分析一下程式的细项.
1.TSR(terminate-and-stay-resident),结束并常驻程式.
DOS是单工作业系统,不像Windows可以多个程式并行,要实作并行,就需要常驻内存,
在某种触发下获得控制权,做完该做的工作后,便交出控制权,继续常驻.
题目要求特定时间下触发,拦截int 2ch(每1/18秒触发一次)是最理想的手段.
结束常驻程式有2种方法实现,一是int 27h,dx=驻留字节数,
另一种是int21h,ah=31h,dx=驻留段数(1段=16bytes)
2.如何读取参数.
一个理想程式,应该是弹性和亲和,比如我们的程式名称是runbat, 用户可以输入
runbat 3:10:30 test.bat abcd 1234
意即程式每3小时10分30秒(第1参数)执行一次test.bat(第2参数),
abcd 1234是传给test.bat的参数(若有).
在这里,程式要分析用户的输入,第1个参数是计时,第2个是执行哪一个批次档.
第2参数后的所有东西视为第3参数,也就是批次档的参数
3.如何执行批次档
dos系统执行程式,可以用int21h,ax=4b00h,ds:dx指向待执行的程式,es:bx指向一个叫psb的结构体
psb label word
dw ?? ;程式参数的偏移值,若和母程式一致, =80h
dw ?? ;程式参数的段值,,若和母程式一致,就是psp段值
dw ?? ;程式第1个fcb偏移值,若和母程式一致, =5ch
dw ?? ;程式第1个fcb段值,若和母程式一致,就是psp段值
dw ?? ;程式第2个fcb偏移值,若和母程式一致, =6ch
dw ?? ;程式第2个fcb段值,若和母程式一致,就是psp段值
es:bx 指向psb即可
但问题是int21h,ax=4b00h只能执行com或exe档,不能执行bat批次档.
执行bat批次档是命令解释器的责任,也就是dos的command.com(或command.exe),
或者windows的cmd.exe
我们在c:\> 之下键入什么,copy也好,test.exe也好,test.bat也好,其实都是命令解释器去执行.
题目说是纯dos,那么只要找到command.com,再把参数传递给它,这就相当于dos下键入批次档的名称.
4.如何找到command.com ?
省时的方式是直接指向启动磁盘的根目录,比如A:\或者C:\
但这样不具弹性,因为它可能根本不在哪里.合理的方法是由环境变数去找
DOS有一个长长的环境变数字串列,记录了许多环境变数,比如PATH,PROMPT等,
这其中,ComSpec就是命令解释器的路径,若在dos下键入set,列出的一堆字串中,会看见
ComSpec=A:\command.com (若在windows下会是ComSpec=C:\WINDOWS\system32\cmd.exe)
环境变数在哪里找?
环境变数的地址在本程式psp:2ch
程式在载入之初,ds,es会指向psp,或在任何时刻,int21h,ah=6ch,bx传回自身程式psp段值,
psp:2ch就是环境变数地址,然后由这开始,找寻 [ComSpec=] ,每一个环境变数以0结束,直至碰到00(双0),
就是整条环境变数的结束.
找到命令解释器路径,用int21h,ax=4b00h载入,再把自己参数2(批次档名)和其后的参数传给command.com
5.防止dos重进入.
还有一个TSR程式一定要面对的问题.
比如我们要写一个dos下的抓图程式,首先设定一些热键,再拦下了int9(键盘中断),然后结束并常驻...
然后用户要抓图时就按下热键,常驻程式被叫出,抓图,编码,存档....做完了才把控制权归还.
好了,问题就在于存档.存档必须使用DOS的,3CH,3DH,3FH,5BH,4EH,4FH等等档案函数,
DOS先天的设计是不可重进入,万一按下热键的刹那,其实仍在DOS的内部中,然后你又去呼叫DOS函数进行存档,
结果就是一个灾难!
但,其实DOS函数00H-0CH和DOS那些档案函数并不使用同一个栈,只要确定热键被呼叫时是在00-0CH的核心程式中,
那么DOS档案函数就可以安全使用.DOS在00-0CH函数运行时,总会触动一下INT28H,原来的INT28H通常是一个IRET,直接返回.
我们只要拦截了28H
mov ax,3528h ;读取原来的INT28H
int 21h
mov int28_ip,bx ;保存原来IP
mov int28_cs,es ;保存原来CS
PUSH CS ;取CS
POP DS ;存入DS,用意就是
mov dx,offset new_int28 ;指向自己的新INT28H - DS:DX
mov ax,2528h ;设定
int 21h
然后,新的INT28H大概是这样,当然是简化许多
New_int28:
sti
PUSHF
PUSH DS
PUSH ES
call main_tsr ;呼叫我们的常驻主程序
POP ES
POP DS
POPF
IRET
当然,纯然INT28H是远远不够的,DOS还有一个叫DOS闲置的标志,
mov ah,34h
int 21h
mov dos_busy_seg,es ;传回闲置标志的段地址在ES,保存
mov dos_busy_off,bx ;传回闲置标志的偏移地址在EX,保存
然后,我们只要读取标志值,就知道DOS是否闲置,
mov es,dos_busy_seg
mov bx,dos_busy_off
cmp byte ptr es:[bx],0
jnz not_idle ; 非0则不是闲置
;到此则闲置
call main_tsr ;呼叫我们的常驻主程序
...
...
not_idle:
.....
.....
好了,在TSR防止dos重进入,int28h 和 DOS闲置的标志,双管齐下,
任谁一种合法就可以,利用int21h,ax=04b00h载入程式.
------------------------------------
求一个tsr汇编的源码,整点运行一次批处理文件
需要在一个纯dos环境中隔一段时间运行一次批处理文件,因为系统需要一直运行另一个程序,
想来想去还是用汇编做一个整点调用批处理文件的com程序比较适合.
------------------------------------
这个要求,老手是不屑,新手是不懂,却最适合刚啃完一本汇编书的中手.
我们来分析一下程式的细项.
1.TSR(terminate-and-stay-resident),结束并常驻程式.
DOS是单工作业系统,不像Windows可以多个程式并行,要实作并行,就需要常驻内存,
在某种触发下获得控制权,做完该做的工作后,便交出控制权,继续常驻.
题目要求特定时间下触发,拦截int 2ch(每1/18秒触发一次)是最理想的手段.
结束常驻程式有2种方法实现,一是int 27h,dx=驻留字节数,
另一种是int21h,ah=31h,dx=驻留段数(1段=16bytes)
2.如何读取参数.
一个理想程式,应该是弹性和亲和,比如我们的程式名称是runbat, 用户可以输入
runbat 3:10:30 test.bat abcd 1234
意即程式每3小时10分30秒(第1参数)执行一次test.bat(第2参数),
abcd 1234是传给test.bat的参数(若有).
在这里,程式要分析用户的输入,第1个参数是计时,第2个是执行哪一个批次档.
第2参数后的所有东西视为第3参数,也就是批次档的参数
3.如何执行批次档
dos系统执行程式,可以用int21h,ax=4b00h,ds:dx指向待执行的程式,es:bx指向一个叫psb的结构体
psb label word
dw ?? ;程式参数的偏移值,若和母程式一致, =80h
dw ?? ;程式参数的段值,,若和母程式一致,就是psp段值
dw ?? ;程式第1个fcb偏移值,若和母程式一致, =5ch
dw ?? ;程式第1个fcb段值,若和母程式一致,就是psp段值
dw ?? ;程式第2个fcb偏移值,若和母程式一致, =6ch
dw ?? ;程式第2个fcb段值,若和母程式一致,就是psp段值
es:bx 指向psb即可
但问题是int21h,ax=4b00h只能执行com或exe档,不能执行bat批次档.
执行bat批次档是命令解释器的责任,也就是dos的command.com(或command.exe),
或者windows的cmd.exe
我们在c:\> 之下键入什么,copy也好,test.exe也好,test.bat也好,其实都是命令解释器去执行.
题目说是纯dos,那么只要找到command.com,再把参数传递给它,这就相当于dos下键入批次档的名称.
4.如何找到command.com ?
省时的方式是直接指向启动磁盘的根目录,比如A:\或者C:\
但这样不具弹性,因为它可能根本不在哪里.合理的方法是由环境变数去找
DOS有一个长长的环境变数字串列,记录了许多环境变数,比如PATH,PROMPT等,
这其中,ComSpec就是命令解释器的路径,若在dos下键入set,列出的一堆字串中,会看见
ComSpec=A:\command.com (若在windows下会是ComSpec=C:\WINDOWS\system32\cmd.exe)
环境变数在哪里找?
环境变数的地址在本程式psp:2ch
程式在载入之初,ds,es会指向psp,或在任何时刻,int21h,ah=6ch,bx传回自身程式psp段值,
psp:2ch就是环境变数地址,然后由这开始,找寻 [ComSpec=] ,每一个环境变数以0结束,直至碰到00(双0),
就是整条环境变数的结束.
找到命令解释器路径,用int21h,ax=4b00h载入,再把自己参数2(批次档名)和其后的参数传给command.com
5.防止dos重进入.
还有一个TSR程式一定要面对的问题.
比如我们要写一个dos下的抓图程式,首先设定一些热键,再拦下了int9(键盘中断),然后结束并常驻...
然后用户要抓图时就按下热键,常驻程式被叫出,抓图,编码,存档....做完了才把控制权归还.
好了,问题就在于存档.存档必须使用DOS的,3CH,3DH,3FH,5BH,4EH,4FH等等档案函数,
DOS先天的设计是不可重进入,万一按下热键的刹那,其实仍在DOS的内部中,然后你又去呼叫DOS函数进行存档,
结果就是一个灾难!
但,其实DOS函数00H-0CH和DOS那些档案函数并不使用同一个栈,只要确定热键被呼叫时是在00-0CH的核心程式中,
那么DOS档案函数就可以安全使用.DOS在00-0CH函数运行时,总会触动一下INT28H,原来的INT28H通常是一个IRET,直接返回.
我们只要拦截了28H
mov ax,3528h ;读取原来的INT28H
int 21h
mov int28_ip,bx ;保存原来IP
mov int28_cs,es ;保存原来CS
PUSH CS ;取CS
POP DS ;存入DS,用意就是
mov dx,offset new_int28 ;指向自己的新INT28H - DS:DX
mov ax,2528h ;设定
int 21h
然后,新的INT28H大概是这样,当然是简化许多
New_int28:
sti
PUSHF
PUSH DS
PUSH ES
call main_tsr ;呼叫我们的常驻主程序
POP ES
POP DS
POPF
IRET
当然,纯然INT28H是远远不够的,DOS还有一个叫DOS闲置的标志,
mov ah,34h
int 21h
mov dos_busy_seg,es ;传回闲置标志的段地址在ES,保存
mov dos_busy_off,bx ;传回闲置标志的偏移地址在EX,保存
然后,我们只要读取标志值,就知道DOS是否闲置,
mov es,dos_busy_seg
mov bx,dos_busy_off
cmp byte ptr es:[bx],0
jnz not_idle ; 非0则不是闲置
;到此则闲置
call main_tsr ;呼叫我们的常驻主程序
...
...
not_idle:
.....
.....
好了,在TSR防止dos重进入,int28h 和 DOS闲置的标志,双管齐下,
任谁一种合法就可以,利用int21h,ax=04b00h载入程式.