通过IO口对硬盘绝对扇区读写

http://read.pudn.com/downloads79/sourcecode/hack/crack/301277/%E7%A1%AC%E7%9B%98%E7%9B%B4%E6%8E%A5IO.asm__.htm

通过IO口对硬盘绝对扇区读写2006-09-20 17:18对硬盘进行操作的常用端口是1f0h~1f7h号端口,各端口含义如下:
端口号     读还是写   具体含义
1F0H       读/写      用来传送读/写的数据(其内容是正在传输的一个字节的数据)
1F1H       读         用来读取错误码
1F2H       读/写      用来放入要读写的扇区数量
1F3H       读/写      用来放入要读写的扇区号码
1F4H       读/写      用来存放读写柱面的低8位字节
1F5H       读/写      用来存放读写柱面的高2位字节(其高6位恒为0)
1F6H       读/写      用来存放要读/写的磁盘号及磁头号
                    第7位     恒为1
                    第6位     恒为0
                    第5位     恒为1
                    第4位     为0代表第一块硬盘、为1代表第二块硬盘
                    第3~0位    用来存放要读/写的磁头号
1f7H       读         用来存放读操作后的状态
                    第7位     控制器忙碌
                    第6位     磁盘驱动器准备好了
                    第5位     写入错误
                    第4位     搜索完成
                    第3位     为1时扇区缓冲区没有准备好
                    第2位     是否正确读取磁盘数据
                    第1位     磁盘每转一周将此位设为1,
                    第0位     之前的命令因发生错误而结束
         写         该位端口为命令端口,用来发出指定命令
                    为50h     格式化磁道
                    为20h     尝试读取扇区
                    为21h     无须验证扇区是否准备好而直接读扇区
                    为22h     尝试读取长扇区(用于早期的硬盘,每扇可能不是512字节,而是128字节到1024之间的值)
                    为23h     无须验证扇区是否准备好而直接读长扇区
                    为30h     尝试写扇区
                    为31h     无须验证扇区是否准备好而直接写扇区
                    为32h     尝试写长扇区
                    为33h     无须验证扇区是否准备好而直接写长扇区
注:当然看完这个表你会发现,这种读写端口的方法其实是基于磁头、柱面、扇区的硬盘读写方法,不过大于8G的硬盘的读写方法也是通过端口1F0H~1F7H来实现的^_^

一个通过对硬盘输入输出端口操作来读写硬盘的实例
让我们来看一个关于INT13H读写硬盘程序实例。在例子中详细说明了硬盘的读写操作所用到的端口,并且把通过INT13H读出的主引导区得到的数据和通过输入输出读主引导区得到的数据进行比较,从而证实这两种操作功能相同,程序片段如下:

mov dx,1f6h ; 要读入的磁盘号及磁头号
mov al,0a0h ;磁盘0,磁头0
out dx,al

mov dx,1f2h ;要读入的扇区数量
mov al,1 ;读一个扇区
out dx,al

mov dx,1f3h ;要读的扇区号
mov al,1 ;扇区号为1
out dx,al

mov dx,1f4h ;要读的柱面的低8位
mov al,0 ; 柱面低8位为0
out dx,al

mov dx,1f5h ; 柱面高2位
mov al,0 ; 柱面高2位为0(通过1F4H和1F5H端口我们可以确定
; 用来读的柱面号是0)
out dx,al

mov dx,1f7h ;命令端口
mov al,20h ; 尝试读取扇区
out dx,al
still_going:
in al,dx
test al,8 ;扇区缓冲是否准备好
jz still_going ;如果扇区缓冲没有准备好的话则跳转,直到准备好才向下执行。

mov cx,512/2 ;设置循环次数(512/2次)
mov di,offset buffer
mov dx,1f0h ;将要传输的一个字节的数据
rep insw ;传输数据

; ——

mov ax,201h ;以下是用INT13H读硬盘的0磁头、0柱面、1扇区
mov dx,80h
mov cx,1
mov bx,offset buffer2
int 13h

mov cx,512 ;以下部分用来比较2种方法读出的硬盘数据
mov si,offset buffer
mov di,offset buffer2
repe cmpsb
jne failure
mov ah,9
mov dx,offset readmsg
int 21h
jmp good_exit
failure:
mov ah,9
mov dx,offset failmsg
int 21h
good_exit: ;以下部分用来结束程序
mov ax,4c00h ;退出程序
int 21h

readmsg db ‘The buffers match. Hard disk read using ports.$’
failmsg db ‘The buffers do not match.$’
buffer db 512 dup (‘V’)
buffer2 db 512 dup (‘L’)

附:
我的“硬盘绝对扇区检测”功能代码:
平台:Fedora Core 5
NASM version 0.98.39
gcc 版本 4.1.0 20060304 (Red Hat 4.1.0-3)
编译命令:
由于有端口读写,所以需要在root下运行
nasm -f elf *.asm
gcc *.o
./a.out
代码:
global main
extern printf
extern ioperm
section .data
dmsg db ‘The buffers match. The sector is good.’,0Dh,0Ah,0
failmsg db ‘The buffers do not match. The sector is bad.’,0Dh,0Ah,0
buf_d times 512 db ‘V’
ct1 db ‘buf_director’,0Dh,0Ah,0
buf_s times 512 db ‘K’
ct2 db ‘buf_source’,0Dh,0Ah,0
buf_g times 512 db ‘G’
ct3 db ‘buf_ghost’,0Dh,0Ah,0
disk db 0b0h ;要读入的磁盘号及磁头号
;第7位 恒为1
;第6位 恒为0
;第5位 恒为1
;第4位 为0代表第一块硬盘、为1代表第二块硬盘
;第3~0位 用来存放要读/写的磁头号
secnum db 1 ;要读入的扇区数量,读一个扇区
secno db 1 ;要读的扇区号,扇区号为1
cylin_l db 0 ;要读的柱面的低8位,柱面低8位为0
cylin_h db 0 ;柱面高2位,柱面高2位为0
section .text
main:
mov ax,ds
mov es,ax

push word 1 ;打开 1f0h-1f7h 端口的读写权限
push dword 08h
push dword 1f0h
call ioperm
add esp,10 ;清空栈

push dword buf_g ;备份数据
pop eax
mov edi,eax
call read
push dword buf_g ;打印
call printf
pop eax

push dword buf_s ;写入数据
pop eax
mov esi,eax
call write
push dword buf_s ;打印
call printf
pop eax

push dword buf_d ;读出数据
pop eax
mov edi,eax
call read
push dword buf_d ;打印
call printf
pop eax

push dword buf_g ;恢复数据
pop eax
mov esi,eax
call write

mov cx,512 ;比较数据,测试是否坏道
push dword buf_d
pop eax
mov esi,eax
push dword buf_s
pop eax
mov edi,eax
repe cmpsb
jne failure

push dword dmsg ;成功,无坏块
call printf
pop eax
jmp exit
failure:
push dword failmsg ;失败,坏块
call printf
pop eax
exit:
ret
read:
mov dx,1f6h ;要读入的磁盘号及磁头号
mov al,[disk]
out dx,al

mov dx,1f2h ;要读入的扇区数量
mov al,[secnum]
out dx,al

mov dx,1f3h ;要读的扇区号
mov al,[secno]
out dx,al

mov dx,1f4h ;要读的柱面的低8位
mov al,[cylin_l]
out dx,al

mov dx,1f5h ;柱面高2位
mov al,[cylin_h]
out dx,al

mov dx,1f7h ;命令端口
mov al,20h ; 尝试读取扇区
;1f7H 读 用来存放读操作后的状态
;第7位 控制器忙碌
;第6位 磁盘驱动器准备好了
;第5位 写入错误
;第4位 搜索完成
;第3位 为1时扇区缓冲区没有准备好
;第2位 是否正确读取磁盘数据
;第1位 磁盘每转一周将此位设为1,
;第0位 之前的命令因发生错误而结束
;写 该位端口为命令端口,用来发出指定命令
;为50h 格式化磁道
;为20h 尝试读取扇区
;为21h 无须验证扇区是否准备好而直接读扇区
;为22h 尝试读取长扇区(用于早期的硬盘,每扇可能不是512字节,而是128字节到1024之间的值)
;为23h 无须验证扇区是否准备好而直接读长扇区
;为30h 尝试写扇区
;为31h 无须验证扇区是否准备好而直接写扇区
;为32h 尝试写长扇区
;为33h 无须验证扇区是否准备好而直接写长扇区
out dx,al
still_going:
in al,dx
test al,8 ;扇区缓冲是否准备好
jz still_going

mov cx,512/2 ;设置循环次数(512/2次)
mov dx,1f0h ;将要传输的一个字节的数据
rep insw ;传输数据
ret

write:
mov dx,1f6h ;要写入的磁盘号及磁头号
mov al,[disk]
out dx,al

mov dx,1f2h ;要写入的扇区数量
mov al,[secnum]
out dx,al

mov dx,1f3h ;要写的扇区号
mov al,[secno]
out dx,al

mov dx,1f4h ;要写的柱面的低8位
mov al,[cylin_l]
out dx,al

mov dx,1f5h ;柱面高2位
mov al,[cylin_h]
out dx,al

mov dx,1f7h ;命令端口
mov al,30h ;尝试写入扇区
out dx,al
still_going_2:
in al,dx
test al,40h ;第6位 磁盘驱动器是否准备好了
jz still_going_2

write_again:
mov cx,512/2 ;设置循环次数(512/2次)
mov dx,1f0h ;将要传输的一个字节的数据
rep outsw ;传输数据

; in al,dx
; test al,20h ;第5位 写入错误
; jz write_again
ret

附2:
硬盘绝对扇区检测程序代码–shell

#!/bin/bash
#$1 起始扇区号,扇区号LBA从 0 开始计
#$2 待测试扇区数
#$3 测试次数

if [ $# -ne 3 ]
then
echo “Useage: ./testdisk StartSectorNo SectorNum TestTimes”
exit $E_BADARGS
fi
let “z = 65” #初始写 ‘A’

a=1
while [ $a -le “$3” ]
do
a=$(($a+1))
######################生成用来测试写入的文件#########################################
let “t = $z / 64”
let “t = $t * 10 + ( $z % 64 / 8 ”
let “t = t * 10 + $z % 8”
rm -f tmp*
touch tmp
echo -ne “$t” >> ./tmp
let “z += 1”
if [ $z = 91 ]
then
let “z = 65”
fi
let “i = 1”
while [ $i -ne 10 ]
do
touch tmp2
cat ./tmp >> ./tmp2
cat ./tmp >> ./tmp2
mv tmp2 tmp
let “i += 1”
done
mv tmp tmp2
let “i = 0”
touch tmp
while [ $i -ne $2 ]
do
cat ./tmp2 >> ./tmp
let “i += 1”
done
############################开始测试写入&&读出&&比较####################################
dd if=./tmp of=/dev/hdb bs=512 count=$2 seek=$1
dd if=/dev/hdb of=./tmp_b bs=512 count=$2 skip=$1
if diff tmp tmp_b >> log
then
echo “***the sector from $1 to $[$1+$2-1] is good*****At the test times: $[$a-1]**********”
else
let “b = 0”
while [ $b -le $(($2-1)) ]
do
b=$(($b+1))
dd if=tmp2 of=/dev/hdb bs=512 count=1 seek=$[$1+$b-1]
dd if=/dev/hdb of=./tmp_s bs=512 count=1 skip=$[$1+$b-1]
if diff tmp2 tmp_s >> log
then
echo “*******the sector $[$1+$b-1] is good*****At the test time: $[$a-1]************************”
else
echo “*******the sector $[$1+$b-1] is bad******At the test time: $[$a-1]************************”
fi
done
fi
done

发表评论

电子邮件地址不会被公开。 必填项已用*标注