标题: 扩展int13读取 MBR(硬盘主引导区)的原程序,端口读硬盘序列号
[打印本页]
作者: qb45
时间: 2004-8-9 00:00
标题: 扩展int13读取 MBR(硬盘主引导区)的原程序,端口读硬盘序列号
基本 BIOS Int 13H 调用是 BIOS 提供的磁盘基本输入输出中断调用, 它可以完成磁盘(包括硬盘和软盘)的复位, 读写, 校验, 定位, 诊断, 格式化等功能.
它使用的就是 CHS 寻址方式, 因此最大识能访问 8 GB 左右的硬盘
扩展 Int13H 接口的目的是为了扩展 BIOS 的功能, 使其支持多于1024柱面的硬盘, 以及可移动介质的琐定, 解锁及弹出等功能
DAP 是基于绝对扇区地址的, 因此利用 DAP, Int13H 可以轻松地逾越 1024 柱面的限制, 因为它根本就不需要 CHS 的概念.
我在win98、dos71下用qb45调试成功
'在qbasic中调用中断,1 必须加入L命令,比如QB/L 2. 必须引用qb.bi',如下
'$INCLUDE: 'qb.bi'
DIM SHARED ax, bx, cx, dx, bp, si, di, ds, flags, es '// 全局共享
BUFFdat$ = STRING$(512, 0) '// 建立一个读写扇区缓冲区(512字节)
d1% = varSEG(buffdat$) '// 获得缓冲区的段地址
d2% = SADD(buffdat$) '// 获得缓冲区的偏移地址
'// 把段地址,偏移地址,读写命令等数据按要求写入数据包(DAP)
dat1$ = CHR$(&H10) + CHR$(0) + MKI$(1) + MKI$(d2%) + MKI$(d1%) + MKL$(0) + MKL$(0)
ds = varSEG(dat1$) '// DS=数据包(DAP)的段地址
si = SADD(dat1$) '// SI=数据包(DAP)的偏移地址
ax = &H4200 '// AX=调用扩展INT13的读扇区命令
dx = &H80 '// DX=第一个硬盘
xint &H13 '// 调用INT13中断
'//------把读出的MBR扇区内容保存到D盘的TESTHD.DAT文件中------
OPEN "d:\testhd.dat" FOR BINARY AS #1
PUT 1,1,BUFFDAT$
CLOSE #1
'//----------------------------------------------------------------------
END
'//中断调用的子程序
SUB xint (num%)
DIM inregs AS RegTypeX
DIM outregs AS RegTypeX
inregs.ax = ax
inregs.bx = bx
inregs.cx = cx
inregs.dx = dx
inregs.si = si
inregs.di = di
inregs.ds = ds
inregs.es = es
CALL INTERRUPTX(num%, inregs, outregs)
ax = outregs.ax
bx = outregs.bx
cx = outregs.cx
dx = outregs.dx
si = outregs.si
di = outregs.di
ds = outregs.ds
es = outregs.es
END SUB
[
Last edited by qb45 on 2006-7-9 at 11:14 ]
作者: Kinglion
时间: 2004-8-23 00:00
支持原创作品!
作者: DOSforever
时间: 2005-5-30 00:00
请问各位,为何我在debug中直接填写要读取扇区的DAP参数而用扩展int13h不行呢?比如,要读出第一个硬盘的第一个物理扇区的内容到3333:3333地址处,DAP数据包是否应该如下:
10 00 01 00 33 33 33 33 00 00 00 00 00 00 00 00
debug
-a
mmmm:nnnn mov ah,42
mmmm:nnnn mov dl,80
mmmm:nnnn mov di,2000
mmmm:nnnn int13
mmmm:nnnn int3
mmmm:nnnn
-e2000 10 00 01 00 33 33 33 33 00 00 00 00 00 00 00 00
-g
执行后在DOS7.10下返回错误,即使查看内存地址3333:3333处也无要读出的数据。在Win98的DOS窗口下死机,热启动也不行。
作者: qb45
时间: 2005-6-10 00:00
磁盘地址数据包 DAP 的结构如下:(DAP 是基于绝对扇区地址的, 可以轻松地逾越1024 柱面的限制)
struct DiskAddressPacket
BYTE PacketSize; // 数据包尺寸:
//(固定值,恒等于16,即10H,指本结构所占用的存储空间)
BYTE Reserved; // ==0
WORD BlockCount; // 要传输的数据块个数(以扇区为单位)
DWORD BufferAddr; // 传输缓冲地址(segment:offset)
QWORD BlockNum; // 磁盘起始绝对块地址
在扩展 Int13H 调用中一般使用如下寄存器约定:
ds:si ==> 磁盘地址数据包( disk address packet )
dl ==> 驱动器号
ah ==> 功能代码 / 返回码
请你仔细看看扩展INT13的调用规范,你会发现
ds:si ==> 磁盘地址数据包
而你的程序中是
mmmm:nnnn mov di,2000
你把SI弄成DI了! 如果你懂一点点Qbasic的话,就能从我的程序中发现你的这个错误!
[此贴子已经被作者于2005-6-10 17:32:54编辑过]
作者: DOSforever
时间: 2005-6-30 00:00
多谢楼上的解答,按照你的方法果然能够成功的读取了!
但是一开始我所得到的资料中确实是写 DI 的,而且在该文中不止一处提到。以下是我找到的相关资料,几乎所以网上的关于扩展 int13h 详细说明都是源自这同一篇文章。后来我根据你指出的错误又再看了一遍,发现在“三. 接口规范”这一节中也确实是写 “ds:SI == 磁盘地址数据包” 的。但是在它后面的“3. API 详解”中所有的入口参数中又都写成“ds:DI = 磁盘地址数据包”。我不知道这是作者的笔误还是我的理解错了?
www.kaijia.net/info_ ...
[此贴子已经被作者于2005-6-30 9:43:01编辑过]
[
Last edited by dosforever on 2005-7-22 at 12:57 ]
作者: qb45
时间: 2005-7-5 00:00
我有着和你相同的经历,我当时要用QBASIC语言做一个磁盘编辑器,因为老的INT13有限制,所以我到处去找资料,在本论坛我也发帖子问过哪里能找到这样的资料,结果有个网友给了我一个老外的中断大全网址,资料非常全,可惜我看不懂E文,后来在罗云彬的汇编找到了中文资料,我就参考这个资料用QBASIC来试验读一个扇区,结果不成功,后来我实验了N次,死鸡了N次,头发白了N根,当时我真以为QBASIC没有这个能力,又想了想,都是编程语言,QBASIC应该能做到,没有做不到的事情,当时没有操作系统的时候,QBASIC还被当作操作系统在用呢,比尔大叔不是说:给我BASIC,没有做不到吗!想到这我就到处去问高手,还好,我在QQ上碰到一个懂编程又懂E文的网友,我请他看看E文帮我翻译,哈哈,原来是中文资料错误!我感觉,这个资料是翻译的人故意弄错的,他可能想,只要你爱好编程,而且肯研究,就能发现错误,实际上这个资料中并不只有这一处错误,最重要的错误是这个,然后还有个错误,在你读前63个扇区的时候没有问题,但是,只要读写大于63个扇区的时候,你就会发现读出来的并不是你要的!这个资料总共有3处错误!你可以看看E文的资料,具体网站我不记得了,不过在本论坛还是能找到我发的求助的帖子,里面有网友给的这个网址。我在《数据恢复论坛》也提到过技术资料下载区,INT13中文资料的错误,他们改了一下,没有改很好,但是在论坛里的编程讨论区有高手写的帖子里把这3个错误都指出来了
作者: TurboY
时间: 2005-8-31 16:08
我也被那个资料给耍了,程序一直出问题都不知道出在哪里,原来就是SI和DI的错了。
看来尽信书不如无书。
作者: qb45
时间: 2005-8-31 16:46
是啊,在很多编程的资料里有些很细小但很关键的错误,有时候作者也不可能去验证它,现在网络流行你COPY我,我COPY他,最后弄得大家都有相同的味道了!
还好的是我只懂qbasic,烦恼相对就少,碰到解决不了的问题,我大不了就说:QBASIC可能没有这个能力
[ Last edited by qb45 on 2005-8-31 at 17:01 ]
作者: GOTOmsdos
时间: 2006-6-30 18:21
没学过QB,试着,好像看懂了,
现在就试一试吧,回头再来。。。。
作者: GOTOmsdos
时间: 2006-6-30 19:45
成功了!
另:
我不懂QB,不知道QB能不能一次就把地址取出来?
,qb45的代码中:
MKI$变量是不是可以从buffdat$中取出一个地址值就可以了?
下面的代码却分成了两次:
d1% = varSEG(buffdat$)
d2% = SADD(buffdat$)
因为,buffdat创建后,他的地址已固定。
没必要分解他的地址吧?
作者: GOTOmsdos
时间: 2006-6-30 20:36
用C语言调用扩展13中断,读写大硬盘已解决拉!
主要代码如下:(测试正确)
#include <dos.h>
#include <stdio.h>
void main(){
unsigned char buf[51200];
FILE *f;
union REGS in,out;
struct DiskAddressPacket
{
unsigned char PacketSize; /* 数据包尺寸(16字节) */
unsigned char Reserved; /* ==0 */
unsigned int BlockCount; /* 要传输的数据块个数(以扇区为单位) */
unsigned long BufferAddr;/* 传输缓冲地址(segment:offset) */
unsigned long BlockNum;/* 磁盘起始绝对块地址 */
/*unsigned long ab; */
}dap={16,0,100,0,16434495};
dap.BufferAddr=(unsigned long)buf;
in.h.ah = 0x42;
in.h.dl = 0x80;
in.x.si = &dap;
/*磁盘地址数据包(Disk Address Packet) */
int86(0x13,&in,&out);
f=fopen("int13ext.dat","wb+");
fwrite(buf,512,100,f);
fclose(f);
}
作者: GOTOmsdos
时间: 2006-6-30 20:41
还发现个小奇怪:
在测试我的读写小硬盘的程序中,照理只能读写1023(3FF,1111111111)个柱面(0-1022)
但是,却能读写1024个柱面!
百思不得其解!
谁知道的说一下。。。
作者: GOTOmsdos
时间: 2006-6-30 20:49
TO DOSforever:
在DEBUG中,先要把DAP弄进去吧?
你是怎么弄进去的?
作者: DOSforever
时间: 2006-6-30 22:07
呵呵呵呵……
是弄进去的,我上面不写着是怎么弄进去的吗,注意中间的
-e2000 10 00 01 00 33 33 33 33 00 00 00 00 00 00 00 00
一行。也就是手工把某个特定的参数填进去试验一下 int13h 的扩展调用。
原来我发这个贴的是论坛还是 DVBBS ,后来转成 DISCUZ! 后在排版上就有些变形了,该换行的没换行,都挤到一起了。
作者: GOTOmsdos
时间: 2006-7-1 00:55
哦,刚才没太注意,
现在,知道了,等一下我也试一下。。。
多谢。。
作者: DOSforever
时间: 2006-7-2 05:30
Originally posted by GOTOmsdos at 2006-6-30 20:41:
还发现个小奇怪:
在测试我的读写小硬盘的程序中,照理只能读写1023(3FF,1111111111)个柱面(0-1022)
但是,却能读写1024个柱面!
百思不得其蠮..
我也不理解你的用意,既然 int13h 扩展调用不再使用CHS参数了,你怎么知道能读写1024个柱面呢?(应该说是
第1024个柱面吧)难道你还换算过?
作者: qb45
时间: 2006-7-2 17:47
Originally posted by GOTOmsdos at 2006-6-30 07:45 PM:
成功了!
另:
我不懂QB,不知道QB能不能一次就把地址取出来?
,qb45的代码中:
MKI$变量是不是可以从buffdat$中取出一个地址值就可以了?
下頮..
我的程序只是从原理上演示一下,只想能实现扩展INT13的读写,并没有做优化什么的,如果要精简,只有把MKI$(d2%) 改为MKI$(SADD(buffdat$)) 实际上没有什么大的意义,但表面上看确实少了两个变量!
我觉得编程中,做硬盘小工具是很有意思的,而且很有价值。
在486的电脑上,BIOS很老,不支持扩展INT13,所以不能读写大硬盘,但是很有意思,我们可以利用IO端口在486电脑上来实现读写137G以内容量的硬盘。
希望大家能互相研究研究!
作者: qb45
时间: 2006-7-2 17:50
也不知道大家对端口读写硬盘感兴趣不
作者: zyl910
时间: 2006-7-2 18:16
Originally posted by qb45 at 2006-7-2 17:50:
也不知道大家对端口读写硬盘感兴趣不
极有兴趣!
找这方面资料找了很久了
作者: darkradx
时间: 2006-7-3 00:58
I/O方法, CIH里面似乎有用过
作者: GOTOmsdos
时间: 2006-7-3 02:01
Originally posted by DOSforever at 2006-7-2 05:30 AM:
我也不理解你的用意,既然 int13h 扩展调用不再使用CHS参数了,你怎么知道能读写1024个柱面呢?(应该说是第1024个柱面吧)难道你还换算过?
对啊,这几天在研究硬盘读写,有以换算到极限进行测试的。。
作者: asbai
时间: 2006-7-4 00:23
Originally posted by DOSforever at 2005-6-30 00:00:
多谢楼上的解答,按照你的方法果然能够成功的读取了!
但是一开始我所得到的资料中确实是写 DI 的,而且在该文中不止一处提到。以下是我找到的 ...
所以说这种技术规范一定要看官方标准的~~

作者: darkradx
时间: 2006-7-6 21:02
作者: qb45
时间: 2006-7-9 10:56
获得硬盘序列号(原程序)
这是一个用端口对硬盘编程例子
本程序在必须在DOS下运行,我在qb4.5版本下运行通过
主板上共有两个IDE接口,每个接口上又分主、从,所以可以接4个IDE设备,这4个端口号各不同,本程序用端口1F0-1F7是主板上的第一个IDE接口上的主接口,最常用的就是这个了。别的只是端口号不一样,编程方法原理一样。
'获得硬盘序列号的程序(非逻辑盘卷标,有的把C盘的卷标说成是硬盘序列号)
'代码:QBASIC,运行环境:DOS
OUT &H1F6, &HA0
OUT &h1F2,1
OUT &H1F3, 1
OUT &h1F4,1
OUT &h1F5,1
OUT &H1F7, &HEC '获得硬盘信息的命令
DO WHILE flag <> &H58
f lag = INP(&H1F7)
if inkey$=chr$(27) then '如果按ESC键,终止程序
print "无法获得硬盘序列号"
end
end if
LOOP
re$ = SPACE$(18)
FOR i% = 1 TO 18
READ a$
H$ = CHR$(VAL("&H" + a$))
MID$(re$, i%, 1) = H$
NEXT i%
duan% = varSEG(re$):offe% = SADD(re$)
DEF SEG = duan%
print "本硬盘的序列号为 ";
FOR i = 1 TO 16
CALL Absolute(r%, offe%) '调用在qb中的内嵌汇编机器码
r1% = r% AND &HFF
r2% = (r% AND &HFF00) / &H100
IF i > 9 AND i < 15THEN PRINT CHR$(r1%); CHR$(r2%);
NEXT i
DEF SEG
END
'此DATA中的数据为机器码,用于读端口字数据(qb中的端口语句只能按字节读写端口,不能按字读写)
DATA 55,89,e5,ba,f0,01,ed,86,e0,8b,5e,06,89,07,5d,ca,02 ,00
作者: GOTOmsdos
时间: 2006-7-13 20:40
支持QB45。。
作者: ninao99
时间: 2008-10-27 19:13
标题: 也编了个扩展int13 读硬盘程序(在win98 下测试通过)
//rdiskc.c
#include <stdio.h>
#include <math.h>
#include <alloc.h>
#include <string.h>
#ifndef NULL
#define NULL 0
#endif
typedef struct DiskAddressPacket{
char PacketSize;
char Reserved;
unsigned int BlockCount;
unsigned long BufferAddr;
int BlockNum[4];
}*DAP,DiskAddrPack;
extern void DREAD(DAP);
int main(int argc,char *argv[]){
int length,i=0,j=0,k=0,line=16;
unsigned char buffer[512];
DAP psi=(DAP)malloc(sizeof(DiskAddrPack));
if(argc==2&&!strcmp(argv[1],"/?")){
printf("\nbdexpen sector(0x,for 4 section) [line]\n\tline the line on show(must be 16 or 32)");
return 0;
}
if(argc!=5&&argc!=6)
return -1;
for(argv++,i=0;i<4;i++){
length=strlen(*argv);
if(length!=4)
return -2;
for(j=0;j<4;j++){
if((*argv)[j]>='0'&&(*argv)[j]<='9')
(*argv)[j]-='0';
else if((*argv)[j]>='a'&&(*argv)[j]<='f')
(*argv)[j]=(*argv)[j]-'a'+10;
else if((*argv)[j]>='A'&&(*argv)[j]<='F')
(*argv)[j]=(*argv)[j]-'A'+10;
else return -3;
}
psi->BlockNum[3-i]=(int)(*argv)[0]*16*16*16+(int)(*argv)[1]*16*16+(int)(*argv)[2]*16+(int)(*argv)[3];
argv++;
}
if(argc==6){
length=strlen(*argv);
for(j=0,k=0,line=0;j<length;){
if((*argv)[k]<48||(*argv)[k]>57)
return -4;
line+=((int)(*argv)[k++]-48)*(int)(pow(10,--length));
}
printf("%d\n",line);
if(line!=16&&line!=32)
line=16;
}
psi->PacketSize=16;
psi->Reserved=0;
psi->BlockCount=1;
psi->BufferAddr=(unsigned long)buffer;
DREAD(psi);
for(j=0;j<512/line;j++){
printf("%3x:",j*line);
for(k=0;k<line;k++){
printf("%4x",buffer[j*line+k]);
if(k==7||k==15&&line==32||k==23)
printf(" -");
}
printf("\t ");
for(k=0;k<line;k++){
if((buffer[j*line+k])<=128&&buffer[j*line+k]!='\n'&&buffer[j*line+k]!='\t')
printf("%c",buffer[j*line+k]);
else printf("\.");
}
printf("\n");
}
free(psi);
return 0;
}
//rdiska.asm
.model small
.data
.code
public _DREAD
_DREAD proc
push bp
mov bp,sp
push ds
push si
mov ah,42h
mov dl,80h
mov si,word ptr [bp+4]
int 13h
pop si
pop ds
pop bp
ret
_DREAD endp
end
编译连接后可以执行,可以像dos命令一样调用,不过没有写使用方法,大概就是把你要读的扇区的线性地址用16位分4段每段4位用空格隔开输入。另外还设置了一个16行/32行显示的切换开关第5个参数。
作者: netwinxp
时间: 2008-10-29 16:04
在测试我的读写小硬盘的程序中,照理只能读写1023(3FF,1111111111)个柱面(0-1022)
但是,却能读写1024个柱面!
对于支持INT13扩展的,只要最后的DAP地址是一样的,它们访问的就是同一个地方,CHS对它来说并没有多少实际意义,不支持的则会不一样,但问题是对于U盘来说,很多BIOS虽然报告支持INT13E,但事实上却是个假相(这也是U潘启动兼容性差的原因),这个问题的罪魁祸首是BIOS,其实符合ATA标准的硬盘真实的就是使用LBA地址来访问(不是古董型的硬盘真正的物理上不同柱面的扇区数不一样,CHS根本就没有意义)。
在486的电脑上,BIOS很老,不支持扩展INT13,所以不能读写大硬盘,但是很有意思,我们可以利用IO端口在486电脑上来实现读写137G以内容量的硬盘。
不可能的事,486的硬盘控制器芯片最多也就提供LBA28的地址,LBA48所需要的LBA地址是48位,所以无论如何也访问不了>137G的硬盘。
[
Last edited by netwinxp on 2008-11-5 at 17:46 ]
作者: 0zwb
时间: 2008-11-20 17:53
有没有最简单的修改硬盘号的工具?