Board logo

标题: 扩展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
我以前弄过一个ASM的 www.cn-dos.net/forum ...

作者: 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
有没有最简单的修改硬盘号的工具?