📄 dos下实现重新启动或者关机的小程序 .txt
字号:
DOS下实现重新启动或者关机的小程序
作者:阿豆腐 于2008-6-16上传
--------------------------------------------------------------------------------
近日,在DOS下测试某个硬件设备,需要执行DOS下的重新启动。上网搜索,大多是Windows下面的那个shutdown。
经过苦苦探寻,终于找到DOS下的这样工具。其中附带了源程序,下面我就对他的程序做一点简单的分析。原程序名为
shut12.zip,其中提供了源程序。不过他的源程序是 MAGIC ASSEMBLER 编译器编译的,和Masm的语法有很大差别,
我动手将它改造为Masm的格式:
;*****************************************************************************
;*DOS下重新启动/关机的代码。也是一个简单的处理输入参数的例子 SOLD.ASM *
;*****************************************************************************
.model Tiny
.data
WinErr db 'This program cannot be run under Windows.',0ah,0dh,'$'
Txt db 'ShutDown v1.2 www.blacklight.wxs.org',0ah,0dh,'$'
SyntaxTxt db 'Syntax: SHUTDOWN [S(hutdown)|R(estart)]',0ah,0dh,'$'
Question db 'S(hutdown), R(estart), or C(ancel)? $'
NoATX db 'Could not shutdown! No ATX maybe?$',0ah,0dh
KeyOff db 'S'
KeyRes db 'R'
KeyCan db 'C'
KeyEsc db 27
CrLf db 0ah,0d,'$'
FlushMsg1 db 'Flushing SMARTDRV buffers...$'
FlushMsg2 db 'done',0ah,0dh,'$'
.code
Syntax: mov ah,9h
mov dx,offset SyntaxTxt
int 21h
jmp Exit
NoPars: mov ah,9h ;Show question
mov dx,offset(Question)
int 21h
DoAsk: xor ah,ah ;Ask for key
int 16h
cmp al,KeyEsc ;Check if 'Esc'-key pressed
je DoCan
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;Check if 'S'-key pressed
je DoOff
cmp al,KeyRes ;Check if 'R'-key pressed
je DoRes
cmp al,KeyCan ;Check if 'C'-key pressed
je DoCan
jmp DoAsk ;Invalid key pressed, ask again...
ShowKey: mov ah,2h ;Show the pressed key
mov dl,al
int 21h
mov ah,9h ;Show CrLf
mov dx,offset(CrLf)
int 21h
ret ;return
.startup
mov ax,160ah ;check for Windows
int 2fh
cmp ax,0000
jne NoWin
mov dx,offset(WinErr) ;print error message
mov ah,9h
int 21h
jmp Exit
NoWin:
mov ah,9h ;Show program name
mov dx,offset(Txt)
int 21h
mov si,81h
mov al,[si]
cmp al,0Dh ;Check if any parameters given
jz NoPars
mov si,81h ;get parameters
ParLoop: lodsb
cmp al,0d ;if end reached with no result
je Syntax ; show syntax
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;check for S parameter
je DoOffW
cmp al,KeyRes ;check for R parameter
je DoResW
jmp ParLoop ;not recognized,goto next char.
DoRes: call ShowKey
DoResW: call FlushSD
db 0EAh
dw 00000
dw 0FFFFh ; jmpf ffff:0000 this instruction will reboot the computer
DoOff: call ShowKey
DoOffW: jmp ATXOff
DoCan: call ShowKey
Exit:
.exit ;exit to DOS
ATXOff: call FlushSD ;flush smartdrive cache
mov ax,5301h ;Function 5301h: APM ?Connect real-mode interface
xor bx,bx ;Device ID: 0000h (=system BIOS)
int 15h ;Call interrupt: 15h
mov ax,530eh ;Function 530Eh: APM ?Driver version
mov cx,0102h ;Driver version: APM v1.2
int 15h ;Call interrupt: 15h
mov ax,5307h ;Function 5307h: APM ?Set system power state
mov bl,01h ;Device ID: 0001h (=All devices)
mov cx,0003h ;Power State: 0003h (=Off)
int 15h ;Call interrupt: 15h
;if the program is still running here, there was an error...
mov ah,9h
mov dx,offset(NoATX)
int 21h
jmp Exit
FlushSD:
mov ah,9h
mov dx,offset(FlushMsg1)
int 21h
mov ax,4A10h ;flush smartdrv/pccache buffers
mov bx,1h
int 1Ah
mov ah,9h
mov dx,offset(FlushMsg2)
int 21h
ret
end
编译也很顺利,唯独运行就出错。仔细研究静态代码无果,转用Turbo Debugger调试,
发现 jz NoPars 这条指令对应的机器码很奇怪,反编译的结果竟然是
jne 0121 和 jmp 000A 两条指令,莫非是TD的bug。再换用最老实的debug,结果仍然如此。
这时候就开始怀疑是编译器本身的问题了,使用 ml /Fl 查看生成的.lst文件:
011A BE 0081 mov si,81h
011D 8A 04 mov al,[si]
011F 3C 0D cmp al,0Dh ;Check if any parameters given
0121 75 03 E9 FEE4 jz NoPars
真的是生成了这样的代码,难道真的是Masm的bug么?
先冷静下来,继续跟踪代码,这个跳转会导致程序运行到一片“乱七八糟”的地方。
而那个位置实际上应该是我们.code下面的代码,再仔细查看生成的.com文件中,竟然
没有.code下面代码对应的机器码,就是说编译过程中,那一段程序没有生成机器码。
再进一步分析,com文件是没有文件头的,运行期前面256个是dos生成的... ...莫非是
程序结构上的问题,我们上面的程序入口并非在代码段开始处。于是,修改程序如下,
修改处用红色标记
;*****************************************************************************
;*DOS下重新启动/关机的代码。也是一个简单的处理输入参数的例子 SNEW.ASM *
;*****************************************************************************
.model Tiny
.data
WinErr db 'This program cannot be run under Windows.',0ah,0dh,'$'
Txt db 'ShutDown v1.2 www.blacklight.wxs.org',0ah,0dh,'$'
SyntaxTxt db 'Syntax: SHUTDOWN [S(hutdown)|R(estart)]',0ah,0dh,'$'
Question db 'S(hutdown), R(estart), or C(ancel)? $'
NoATX db 'Could not shutdown! No ATX maybe?$',0ah,0dh
KeyOff db 'S'
KeyRes db 'R'
KeyCan db 'C'
KeyEsc db 27
CrLf db 0ah,0d,'$'
FlushMsg1 db 'Flushing SMARTDRV buffers...$'
FlushMsg2 db 'done',0ah,0dh,'$'
.code
.startup
jmp realstart
Syntax: mov ah,9h
mov dx,offset SyntaxTxt
int 21h
jmp Exit
NoPars: mov ah,9h ;Show question
mov dx,offset(Question)
int 21h
DoAsk: xor ah,ah ;Ask for key
int 16h
cmp al,KeyEsc ;Check if 'Esc'-key pressed
je DoCan
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;Check if 'S'-key pressed
je DoOff
cmp al,KeyRes ;Check if 'R'-key pressed
je DoRes
cmp al,KeyCan ;Check if 'C'-key pressed
je DoCan
jmp DoAsk ;Invalid key pressed, ask again...
ShowKey: mov ah,2h ;Show the pressed key
mov dl,al
int 21h
mov ah,9h ;Show CrLf
mov dx,offset(CrLf)
int 21h
ret ;return
realstart:
mov ax,160ah ;check for Windows
int 2fh
cmp ax,0000
jne NoWin
mov dx,offset(WinErr) ;print error message
mov ah,9h
int 21h
jmp Exit
NoWin:
mov ah,9h ;Show program name
mov dx,offset(Txt)
int 21h
mov si,81h
mov al,[si]
cmp al,0Dh ;Check if any parameters given
jz NoPars
mov si,81h ;get parameters
ParLoop: lodsb
cmp al,0d ;if end reached with no result
je Syntax ; show syntax
and al,0DFh ;convert AL to uppercase
cmp al,KeyOff ;check for S parameter
je DoOffW
cmp al,KeyRes ;check for R parameter
je DoResW
jmp ParLoop ;not recognized,goto next char.
DoRes: call ShowKey
DoResW: call FlushSD
db 0EAh
dw 00000
dw 0FFFFh ; jmpf ffff:0000 this instruction will reboot the computer
DoOff: call ShowKey
DoOffW: jmp ATXOff
DoCan: call ShowKey
Exit:
.exit ;exit to DOS
ATXOff: call FlushSD ;flush smartdrive cache
mov ax,5301h ;Function 5301h: APM ?Connect real-mode interface
xor bx,bx ;Device ID: 0000h (=system BIOS)
int 15h ;Call interrupt: 15h
mov ax,530eh ;Function 530Eh: APM ?Driver version
mov cx,0102h ;Driver version: APM v1.2
int 15h ;Call interrupt: 15h
mov ax,5307h ;Function 5307h: APM ?Set system power state
mov bl,01h ;Device ID: 0001h (=All devices)
mov cx,0003h ;Power State: 0003h (=Off)
int 15h ;Call interrupt: 15h
;if the program is still running here, there was an error...
mov ah,9h
mov dx,offset(NoATX)
int 21h
jmp Exit
FlushSD:
mov ah,9h
mov dx,offset(FlushMsg1)
int 21h
mov ax,4A10h ;flush smartdrv/pccache buffers
mov bx,1h
int 1Ah
mov ah,9h
mov dx,offset(FlushMsg2)
int 21h
ret
end
可以看到,我们的修改只是将程序的入口放到了代码段的起始。编译运行,没有报错,感觉很好。
我们再来比较生成com文件的大小:snew.com 449 字节 vs sold.com 389 字节 。明显长了一截~
不见了的代码又重新出现... ...查看jz NoPars 的机器代码是 74 A8 正确,因此,整个问题得以解决。
总结一下:对于com文件,它没有文件头,因此不可以将代码的入口设置在程序中,否则编译过程中
会丢失入口之前的代码,并且程序会将一些跳转误认为是段间的跳转,从而导致许许多多稀奇古怪的问题。
另外,程序提到了smartdrv,这个是dos下的一个硬盘缓冲驱动,加上它之后硬盘的读写会先缓冲到内存中,
从而达到加速的目的,也因为如此,在shutdown/reset的时候要记得“请它先”完成读写动作。另外,APM
是dos下的电源管理方面的服务,是BIOS提供的。具体的解释可以在BIOS中断大全中找到。
--------------------------------------------------------------------------------
<<<上一篇 欢迎访问AoGo汇编小站:http://www.aogosoft.com 下一篇>>>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -