0xAA55 发表于 2015-12-12 01:39:15

【汇编】用nasm构造一个软盘镜像

之前我生成软盘镜像用的是UltraISO,使用它来将一个个文件拷进一个3.5英寸软盘镜像里,就像下图这样。。

偶尔我也通过使用VMware虚拟机生成一个空的软盘镜像,然后用虚拟机Windows将其格式化并往里面添加文件。。
这两种方法都……是手动的。我不喜欢这样。然而我也懒得直接用C或者VB写一个自动的制造软盘镜像的东西。我想用一个更加方便的方式来完成。。
于是我就写了一个汇编的inc文件,用nasm编译一个汇编的“脚本”来生成一个软盘镜像(虽然这似乎更麻烦了)
你只需要包含我的inc然后像这样写代码,就可以生成一个有文件的软盘镜像了!%include"flpbuild.inc"

flp_start

;设置卷标
flp_add_empty_file "FLPBUILD",A_VolLabel

;添加文件
flp_add_file "build.bat",        "BUILD   BAT",A_Normal|A_ReadOnly
flp_add_file "picture.jpg",        "PICTURE JPG",A_Normal
flp_add_file "flpbuild.inc","FLPBUILDINC",A_Normal
flp_add_file "flp.asm",                "FLP   ASM",A_Normal

flp_end(虽然文件不能一个一个地添加,而且也不能添加子文件夹,不过这好歹是可以自动化的)
用这种方法“编译”出来的img文件可以用UltraISO正常打开、查看里面的文件。

软盘使用的是FAT12文件系统,对于这个文件系统,它有个DBR引导扇区,2个FAT表,1个根目录表,和数据区。如何才能让汇编器分别往这几个不同的地方都添加自己想要添加的东西呢?用“段”来实现。

通常情况下我们编写汇编语言程序的时候,都会定义.text段(即代码段)、.data段(数据段)等。不同的段被用于存储不同类型的数据,这些段单独存在,不会被拆开存储。因此我们可以巧妙利用这一特性,通过分段来让汇编器把我们生成的字节丢到不同的位置,最后再把这些段拼接起来,形成一个完整的文件。

然后我们用编译DOS的.COM后缀的程序的方法,让nasm“编译”我们的软盘就行了。生成的文件是.COM程序那样的平坦结构模型,因此所有的段都是直接连在一起的。这正好符合我们的需求——将软盘镜像的DBR扇区、FAT表、根目录、数据区等连接在一起。

现在放上flpbuild.inc的内容——;FLP文件构建器
;作者:0xAA55
;网站:http://www.0xaa55.com
;请保留原作者信息。

;用法:
;1、建立新的汇编文件
;2、包含这个头文件
;3、使用flp_start宏,进行初始化操作
;4、使用flp_add_file宏来添加文件,参数详见宏定义。
;   或者使用flp_add_empty_file来添加空白文件。
;   你可以重复使用上面的宏来添加多个文件。
;   补充:添加文件之前,你还可以调用flp_set_create_time来设置文件的创建时间,或
;   flp_set_last_write_time来设置文件的最后写入时间。
;5、最后,调用flp_end结束(会填充0来完成整个镜像文件的构造)

%macro fn8_3 1
%%SFN db %1
times 11-($-%%SFN) db " "
%endmacro

%define MakeTime(h,m,s) (((s>>1)&0x1f)|((m&0x3f)<<5)|((h&0x1f)<<11))
%define MakeDate(y,m,d) ((d&0x1f)|((m&0xf)<<5)|(((y-1980)&0x7f)<<9))

;文件属性位
%define A_Normal        0x00
%define A_ReadOnly        0x01
%define A_Hidden        0x02
%define A_System        0x04
%define A_VolLabel        0x08
%define A_LFNItem        0x0F
%define A_Directory        0x10
%define A_Archive        0x20

;宏名:flp_set_create_time
;描述:设置文件创建的日期和时间
;参数:
;MakeTime(时间)
;MakeDate(日期)
%macro flp_set_create_time 2
        %define _flp_cdate %1
        %define _flp_ctime %2
%endmacro

;宏名:flp_set_last_write_time
;描述:设置文件创建的日期和时间
;参数:
;MakeTime(时间)
;MakeDate(日期)
%macro flp_set_last_write_time 2
        %define _flp_wdate %1
        %define _flp_wtime %2
%endmacro

;宏名:flp_write_cluster_number
;描述:在FAT表写入簇号
%macro flp_write_cluster_number 1
        %assign cur_clus_num (%1)&0xfff
        %if curclus & 1
                segment fat1
                db last_clus_num & 0xff
                db ((last_clus_num>>8) & 0xf) | ((cur_clus_num & 0xf)<<4)
                db cur_clus_num >> 4
                segment fat2
                db last_clus_num & 0xff
                db ((last_clus_num>>8) & 0xf) | ((cur_clus_num & 0xf)<<4)
                db cur_clus_num >> 4
        %endif
        %assign curclus curclus+1
        %assign last_clus_num cur_clus_num
%endmacro

;宏名:flp_write_cluster_end
;描述:在FAT表簇链中写入末簇
%macro flp_write_cluster_end 0
        flp_write_cluster_number 0xfff
%endmacro

;宏名:flp_write_seq_clus
;描述:在FAT表簇链中写入有序的簇号
;参数:
;簇数
%macro flp_write_seq_clus 1
        %if %1>1
                %rep %1-1
                        flp_write_cluster_number curclus+1
                %endrep
        %endif
        flp_write_cluster_end
%endmacro

;宏名:flp_start
;描述:开始构建FLP文件
%macro flp_start 0
        segment dbr
        incbin"dbr.bin"
        segment fat1
        segment fat2
        segment root
        segment data
       
        flp_set_create_time   MakeDate(2015,12,11),MakeTime(23,0,0)
        flp_set_last_write_time MakeDate(2015,12,11),MakeTime(23,0,0)
       
        %assign curclus 0
        flp_write_cluster_number 0xff0
        flp_write_cluster_number 0xfff
%endmacro

;宏名:flp_add_empty_file
;描述:添加空文件到根目录区
;参数:
;短文件名
;文件属性
%macro flp_add_empty_file 2
        segment root
        fn8_3 %1
        db %2
        db 0
        db 0
        dw _flp_ctime
        dw _flp_cdate
        dw _flp_wdate
        dw 0
        dw _flp_wtime
        dw _flp_wdate
        dw 0
        dd 0
%endmacro

;宏名:flp_add_file
;描述:添加文件到根目录区
;参数:
;文件名
;短文件名
;文件属性
%macro flp_add_file 3
        ;先将文件添加到数据区
        segment data
        %%SOF:
        incbin %1
        %%EOF:
        %%LOF equ %%EOF-%%SOF
        %if %%LOF
                %%StartClus equ (%%SOF-$$)/512+2
                %%NbClus equ (%%LOF-1)/512+1
                times 512*%%NbClus-%%LOF db 0
        %else
                %%StartClus equ 0
                %%NbClus equ 0
        %endif

        ;然后在根目录添加目录项
        segment root
        fn8_3 %2
        db %3
        db 0
        db 0;文件创建时间的十毫秒数
        dw _flp_ctime
        dw _flp_cdate
        dw _flp_wdate
        dw %%StartClus>>16
        dw _flp_wtime
        dw _flp_wdate
        dw %%StartClus
        dd %%LOF
       
        ;之后更新FAT表
        flp_write_seq_clus %%NbClus
%endmacro

;宏名:flp_end
;描述:结束FLP文件的构建,做收尾工作
%macro flp_end 0
        %if curclus & 1
                flp_write_cluster_end
        %endif
        segment fat1
        times 9*512-($-$$) db 0
        segment fat2
        times 9*512-($-$$) db 0
        segment root
        times 14*512-($-$$) db 0
        segment data
        times 2847*512-($-$$) db 0
%endmacro通过使用这些宏,我们就可以生成一个软盘镜像文件了。
用flp_add_empty_file来添加空文件,然后用flp_add_file来添加文件。
但是就目前而言,这东西不能添加文件夹,不能往文件夹里添加文件,并且不支持长文件名。不过我们可以在现在用的这些宏的基础之上,添加一些新的宏,比如,写一个新的宏使其在添加文件的时候,将目录项添加到别的地方而不是root段(这个“别的地方”就是我们的子文件夹的内容了)并修改flp_end来将子文件夹放在数据区的最后(同时还要保证FAT表也同步)。。当然还有更好的办法只是我还没想出来而已。现在先这么用吧。等到需要的时候——我们还是干脆用C语言写个生成软盘镜像的东西吧。



参考资料:
http://www.0xaa55.com/thread-5-1-1.html

类似的例子:使用汇编构造一个ico图标文件或cur光标文件
http://www.0xaa55.com/thread-1204-1-1.html

Geek 发表于 2016-3-8 22:53:19

VERY GOOD!

cyycoish 发表于 2017-5-19 01:03:49

最近装了个OS却发现VMTool不支持,费了很大的劲儿才写了个软盘镜像,最后才把一些东西从VM里边取出来。
结果发现自己白写了,因为A5这边有现成的。但是我也把我的发上来。

; FDDBOOT.IMG
; 05142017
DB 0xEB, 0xFE, 0x90 ; Disassemble code. As an infinite loop.
DB "ABCDEFGH"       ; OEM Identifier. For 8 Bytes.
DW 512            ; Number of each sector. (Must be 512 bytes for FAT12.)
DB 1                ; Number of each cluster. (Which must equal to one sector.)
DW 1                ; Boot record sector offset. (Usually start with the 1st sector.)
DB 2                ; Number of File Allocation tables. (Must be 2 for FAT12.)
DW 224            ; Number of root directories.
DW 2880             ; Size of the disk. (Must be 2880 sectors for FAT12.)
DB 0xF0             ; Media type. (Must be 0xF0 for FAT12.)
DW 9                ; Length of FAT. (Must be 9 sectors for FAT12.)
DW 18               ; How many sectors are there in a track. (Must be 18 for FAT12.)
DW 2                ; Number of disk heads. (Wich must be 2.)
DD 0                ; No hidden sectors. No extra partitions.
DD 2880             ; Disk size again.
DB 0x00             ; 0x00 for a floppy disk. 0x80 for a hard disk.
DB 0x00             ; Reserved. For WinNT use only.
DB 0x29             ; Signature (Must be 0x28 or 0x29.)
DD 0xFFFFFFFF       ; Volume ID.
DB "ABCDEFGHIJ "    ; Disk name. (11 bytes padding with spaces.)
DB "FAT12   "       ; Disk format type. (8 bytes. Padding with spaces.)
RESB 448            ; Boot code.
DB 0x55, 0xAA       ; 0xAA55 signature.
RESB 1474048      ; Reserve byte with 0x00.
; End. 26 lines totally. Please compile with NASM assembler. nasm -o fddboot.img FDDBOOT.ASM

(⊙o⊙) 发表于 2017-11-6 07:51:42

够屌,汇编果然

誓不回头 发表于 2018-5-3 20:37:04

如果做一个VDI或者VMDK镜像,方法大致也是如此??

0xAA55 发表于 2018-5-5 02:03:08

誓不回头 发表于 2018-5-3 20:37
如果做一个VDI或者VMDK镜像,方法大致也是如此??

不一样的。
页: [1]
查看完整版本: 【汇编】用nasm构造一个软盘镜像