参考
https://www.cnblogs.com/wanmeishenghuo/tag/%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/
https://blog.51cto.com/13475106/category6.html
及狄泰软件相关课程
上一节我们进行了文件的查找实验,文件查找成功了,这一节,我们将文件的内容加载进内存,再一次将整体的流程给出如下:
读取文件的内容时,我们需要根据FAT表找到存储文件内容的每一个扇区,然后进行内容的读取,在上一节中,我们将整个目录区的内容加载到了内存并根据文件名找到了所在的目录项,为了节省内存,我们将找到的目录项拷贝到另一片内存区域中,因为这个目录项中还有我们需要的内容,比如文件的起始扇区号等。而原来加载目录区的那一部分内存便可以另作他用,比如可以将FAT表加载到该区域。
目标文件的目录信息如下:
内存拷贝时,为了防止已经拷贝的内容将原来还没有拷贝的内容覆盖,我们需要考虑一下拷贝的方向,示意图如下:
当源地址小于等于目标地址时,要从后向前拷贝,如上图中左半部,当源地址大于目标地址时,要从头部向尾部拷贝,如上图右半部。
用到的汇编指令如下所示:
下面直接给出内存拷贝的汇编代码:
org 0x7c00 jmp short start nop define: BaseOfStack equ 0x7c00 BaseOfLoader equ 0x9000 RootEntryOffset equ 19 RootEntryLength equ 14 EntryItemLength equ 32 FatEntryOffset equ 1 FatEntryLength equ 9 header: BS_OEMName db "D.T.Soft" BPB_BytsPerSec dw 512 BPB_SecPerClus db 1 BPB_RsvdSecCnt dw 1 BPB_NumFATs db 2 BPB_RootEntCnt dw 224 BPB_TotSec16 dw 2880 BPB_Media db 0xF0 BPB_FATSz16 dw 9 BPB_SecPerTrk dw 18 BPB_NumHeads dw 2 BPB_HiddSec dd 0 BPB_TotSec32 dd 0 BS_DrvNum db 0 BS_Reserved1 db 0 BS_BootSig db 0x29 BS_VolID dd 0 BS_VolLab db "D.T.OS-0.01" BS_FileSysType db "FAT12 " start: mov ax, cs mov ss, ax mov ds, ax mov es, ax mov sp, BaseOfStack mov ax, RootEntryOffset mov cx, RootEntryLength mov bx, Buf call ReadSector mov si, Target mov cx, TarLen mov dx, 0 call FindEntry ; cmp dx, 0 ; jz output mov si, Target mov di, MsgStr mov cx, TarLen call MemCpy output: mov bp, MsgStr mov cx, MsgLen call Print last: hlt jmp last ; ds:si --> source ; es:di --> destination ; cx --> length MemCpy: push si push di push cx push ax cmp si, di ja btoe add si, cx add di, cx dec si dec di jmp etob btoe: cmp cx, 0 jz done mov al, [si] mov byte [di], al inc si inc di dec cx jmp btoe etob: cmp cx, 0 jz done mov al, [si] mov byte [di], al dec si dec di dec cx jmp etob done: pop ax pop cx pop di pop si ret ; es:bx --> root entry offset address ; ds:si --> target string ; cx --> target length ; ; return: ; (dx !=0 ) ? exist : noexist ; exist --> bx is the target entry FindEntry: push di push bp push cx mov dx, [BPB_RootEntCnt] mov bp, sp find: cmp dx, 0 jz noexist mov di, bx mov cx, [bp] call MemCmp cmp cx, 0 jz exist add bx, 32 dec dx jmp find exist: noexist: pop cx pop bp pop di ret ; ds:si --> source ; es:di --> destination ; cx --> length ; ; return: ; (cx == 0) ? equal : noequal MemCmp: push si push di push ax compare: cmp cx, 0 jz equal mov al, [si] cmp al, byte [di] jz goon jmp noequal goon: inc si inc di dec cx jmp compare equal: noequal: pop ax pop di pop si ret ; es:bp --> string address ; cx --> string length Print: mov dx, 0 mov ax, 0x1301 mov bx, 0x0007 int 0x10 ret ; no parameter ResetFloppy: push ax push dx mov ah, 0x00 mov dl, [BS_DrvNum] int 0x13 pop dx pop ax ret ; ax --> logic sector number ; cx --> number of sector ; es:bx --> target address ReadSector: push bx push cx push dx push ax call ResetFloppy push bx push cx mov bl, [BPB_SecPerTrk] div bl mov cl, ah add cl, 1 mov ch, al shr ch, 1 mov dh, al and dh, 1 mov dl, [BS_DrvNum] pop ax pop bx mov ah, 0x02 read: int 0x13 jc read pop ax pop dx pop cx pop bx ret MsgStr db "No LOADER ..." MsgLen equ ($-MsgStr) Target db "LOADER " TarLen equ ($-Target) EntryItem times EntryItemLength db 0x00 Buf: times 510-($-$$) db 0x00 db 0x55, 0xaa
以上程序将Target处的字符串拷贝到了MsgStr处,然后将MsgStr处的内容打印出来,结果如下:
下一步,我们进行FAT表项的读取,并根据表项的内容加载文件数据,FAT表项中每个表项占用1.5个字节,即使用3个字节表示两个表项,如下图所示:
以前读取a.img的实验中,我们是将FAT表项完全读取后做了一次转换,生成了一个向量,向量中的每一项占用2个字节,那是一次性将所有FAT表项转换好的,而在我们的汇编程序中,我们使用动态组装的方法,即每用到一个表项就进行临时组装,组装后的FAT表项的下标和起始字节的关系图如下:
组装时用到的关系式如下所示:
需要用到的乘法汇编指令如下:
我们先打开之前写的读取a.img的Qt程序,添加一行,如下所示:
我们新添加了224行,将数据所在的扇区号分别打印出来,运行程序,得到如下结果:
上图中只打印出了4,说明数据只存在第4扇区,也就是222行的循环只循环了一次,第二次循环是vec[j]已经不再小于0xFF7,也就是FAT表项不再是一个有效项,而是一个结束项。
下面给出汇编程序:
org 0x7c00 jmp short start nop define: BaseOfStack equ 0x7c00 BaseOfLoader equ 0x9000 RootEntryOffset equ 19 RootEntryLength equ 14 EntryItemLength equ 32 FatEntryOffset equ 1 FatEntryLength equ 9 header: BS_OEMName db "D.T.Soft" BPB_BytsPerSec dw 512 BPB_SecPerClus db 1 BPB_RsvdSecCnt dw 1 BPB_NumFATs db 2 BPB_RootEntCnt dw 224 BPB_TotSec16 dw 2880 BPB_Media db 0xF0 BPB_FATSz16 dw 9 BPB_SecPerTrk dw 18 BPB_NumHeads dw 2 BPB_HiddSec dd 0 BPB_TotSec32 dd 0 BS_DrvNum db 0 BS_Reserved1 db 0 BS_BootSig db 0x29 BS_VolID dd 0 BS_VolLab db "D.T.OS-0.01" BS_FileSysType db "FAT12 " start: mov ax, cs mov ss, ax mov ds, ax mov es, ax mov sp, BaseOfStack mov ax, RootEntryOffset mov cx, RootEntryLength mov bx, Buf call ReadSector mov si, Target mov cx, TarLen mov dx, 0 call FindEntry cmp dx, 0 jz output mov si, bx mov di, EntryItem mov cx, EntryItemLength call MemCpy mov ax, FatEntryLength mov cx, [BPB_BytsPerSec] mul cx mov bx, BaseOfLoader sub bx, ax mov ax, FatEntryOffset mov cx, FatEntryLength call ReadSector mov cx, [EntryItem + 0x1A] call FatVec jmp last output: mov bp, MsgStr mov cx, MsgLen call Print last: hlt jmp last ; cx --> index ; bx --> fat table address ; ; return: ; dx --> fat[index] FatVec: mov ax, cx mov cl, 2 div cl push ax mov ah, 0 mov cx, 3 mul cx mov cx, ax pop ax cmp ah, 0 jz even jmp odd even: ; FatVec[j] = ( (Fat[i+1] & 0x0F) << 8 ) | Fat[i]; mov dx, cx add dx, 1 add dx, bx mov bp, dx mov dl, byte [bp] and dl, 0x0F shl dx, 8 add cx, bx mov bp, cx or dl, byte [bp] jmp return odd: ; FatVec[j+1] = (Fat[i+2] << 4) | ( (Fat[i+1] >> 4) & 0x0F ); mov dx, cx add dx, 2 add dx, bx mov bp, dx mov dl, byte [bp] mov dh, 0 shl dx, 4 add cx, 1 add cx, bx mov bp, cx mov cl, byte [bp] shr cl, 4 and cl, 0x0F mov ch, 0 or dx, cx return: ret ; ds:si --> source ; es:di --> destination ; cx --> length MemCpy: push si push di push cx push ax cmp si, di ja btoe add si, cx add di, cx dec si dec di jmp etob btoe: cmp cx, 0 jz done mov al, [si] mov byte [di], al inc si inc di dec cx jmp btoe etob: cmp cx, 0 jz done mov al, [si] mov byte [di], al dec si dec di dec cx jmp etob done: pop ax pop cx pop di pop si ret ; es:bx --> root entry offset address ; ds:si --> target string ; cx --> target length ; ; return: ; (dx !=0 ) ? exist : noexist ; exist --> bx is the target entry FindEntry: push di push bp push cx mov dx, [BPB_RootEntCnt] mov bp, sp find: cmp dx, 0 jz noexist mov di, bx mov cx, [bp] call MemCmp cmp cx, 0 jz exist add bx, 32 dec dx jmp find exist: noexist: pop cx pop bp pop di ret ; ds:si --> source ; es:di --> destination ; cx --> length ; ; return: ; (cx == 0) ? equal : noequal MemCmp: push si push di push ax compare: cmp cx, 0 jz equal mov al, [si] cmp al, byte [di] jz goon jmp noequal goon: inc si inc di dec cx jmp compare equal: noequal: pop ax pop di pop si ret ; es:bp --> string address ; cx --> string length Print: mov dx, 0 mov ax, 0x1301 mov bx, 0x0007 int 0x10 ret ; no parameter ResetFloppy: push ax push dx mov ah, 0x00 mov dl, [BS_DrvNum] int 0x13 pop dx pop ax ret ; ax --> logic sector number ; cx --> number of sector ; es:bx --> target address ReadSector: push bx push cx push dx push ax call ResetFloppy push bx push cx mov bl, [BPB_SecPerTrk] div bl mov cl, ah add cl, 1 mov ch, al shr ch, 1 mov dh, al and dh, 1 mov dl, [BS_DrvNum] pop ax pop bx mov ah, 0x02 read: int 0x13 jc read pop ax pop dx pop cx pop bx ret MsgStr db "No LOADER ..." MsgLen equ ($-MsgStr) Target db "LOADER " TarLen equ ($-Target) EntryItem times EntryItemLength db 0x00 Buf: times 510-($-$$) db 0x00 db 0x55, 0xaa
运行bochs,在程序的第77,79行打断点(具体方法见上节反汇编ndisasm使用方法),然后运行到第一个断点处,打印寄存器内容如下:
可以看到ecx中的后两个字节(bochs为16字节操作)确实为4,这就是文件数据起始扇区号,edx此时还为0,继续运行到下一个断点处,打印寄存器如下:
寄存器edx的内容为0xfff,这就是FAT表项中的内容,说明文件结束了,文件的内容只存在了一个扇区中。本节中的FatVec函数并没有完善,本节还没有进行文件内容的读取,只是根据文件数据起始扇区号,读取了下一个FAT表项的内容,下一节继续。。。