X86-asm代码使USB磁盘不可启动

X86-asm code to make a usb disk non-bootable

我们已经在基于自定义 MCU 的硬件上开发了 USB 复合设备(CDC 和 MSD 类)。该磁盘是一个 RAM 磁盘(space 的 24 kb)并且在通电时,它被 MCU 供应商固件格式化(FAT 12)。不幸的是,该设备被视为可引导磁盘,典型的 BIOS 会尝试引导它。然而,我们知道将各种 USB 磁盘连接到不同的 PC 会产生不同的结果,这取决于磁盘 FAT 类型和 BIOS 行为(显然每个 BIOS 都先于 HDD 启动 USB)。 我们的目标是无法启动 USB 磁盘(FAT12 格式)

我们尝试了很多解决方案,但 none 奏效了。 在我们看来,BIOS 似乎不仅仅是检查有效引导扇区的存在,将第一个扇区复制到地址 0x7C00 并跳转到该地址,从而将控制权交给 x86 ASM 引导代码。 事实上,我们已经用命令格式化了一个商用U盘Linux:

mkdosfs -F 12 /dev/sda1

此 U 盘现在有一个 FAT12 卷启动记录,我们迄今为止还没有发现在尝试从它启动时卡住的 BIOS

因此,我们将其引导扇区复制到我们的设备中,并希望它能像商用 U 盘一样工作。不:BIOS 在尝试引导磁盘时挂起。下面是对上述Linux命令生成的引导扇区的反汇编

;Jump instructions
0x00007c00 eb 3c                            jmp    0x00007c3e
0x00007c02 90                               nop

;VBR segment
0x00007c03 6d                               
0x00007c04 6b 66 73 2e                      
0x00007c08 66 61                              

..............

;Boot code
0x00007c3e 0e                               push   %cs
0x00007c3f 1f                               pop    %ds
0x00007c40 be 5b 7c                         mov    [=11=]x7c5b,%si
0x00007c43 ac                               lods   %ds:(%si),%al
0x00007c44 22 c0                            and    %al,%al
0x00007c46 74 0b                            je     0x00007c53
0x00007c48 56                               push   %si
0x00007c49 b4 0e                            mov    [=11=]xe,%ah
0x00007c4b bb 07 00                         mov    [=11=]x7,%bx
0x00007c4e cd 10                            int    [=11=]x10
0x00007c50 5e                               pop    %si
0x00007c51 eb f0                            jmp    0x00007c43
0x00007c53 32 e4                            xor    %ah,%ah
0x00007c55 cd 16                            int    [=11=]x16
0x00007c57 cd 19                            int    [=11=]x19
0x00007c59 eb fe                            jmp    0x00007c59

;String to be displayed followed by zeros

0x00007c5b 54                               
0x00007c5c 68 69 73                         

..........

;Boot signature
0x00007dfe 55                               
0x00007dff aa                               

显然,上面的代码打印了字符串"This is not a bootable disk. Please insert a bootable floppy and press any key to try again",然后调用了INT16H和INT19H。

为什么我正在写入的 PC 的 BIOS 打印:"Attempting boot from USB disk" 然后跳转到我的硬盘,而我们的设备中写入的相同汇编代码却没有?也就是写"Please insert..."?是否存在可以替代此代码并且永远不会尝试启动我的自定义 USB 磁盘的代码?

此外,BIOS是否在读取第一个扇区之前与USB设备进行一些特殊的通信,例如,从设备描述符中获取一些信息,以了解它是否可以用作启动盘?

提前谢谢大家。

尝试调用INT 18H,这是BIOS Boot Specification (Compaq, Phoenix, Intel 1996)指定的尝试下一个启动设备。

或者它可能需要使用 INT 13H 自行加载下一个磁盘的引导扇区。

最终,可能没有任何东西可以阻止您的自定义 USB 设备在可能插入的任何 PC 上用作启动设备。但是,您可以做几件事来涵盖大多数情况,首先是在引导扇区的末尾不要有 0xAA55 签名,其次是调用引导扇区的 INT 0x18 以要求 BIOS 尝试下一个启动装置。

BIOS 如何启动 USB 设备

这里的基本问题是 USB 大容量存储设备的 BIOS 引导没有标准。 BIOS 究竟如何确定 USB 设备是否可启动因 BIOS 实现而异。然而,所有实施都将遵循两个基本步骤。

首先是确定是否将USB设备模拟为"floppy"、"hard drive"、"cdrom"、"zip"或其他类型的驱动器。对于您的设备,它应该归结为在软盘或硬盘驱动器仿真之间进行选择。做出此选择可能有多种标准,例如驱动器的大小、其是否将自身报告为可移动以及使用的命令集。特别是软盘仿真可以支持的驱动器大小有一个上限,我相信大约是 530MB,这个大小通常用于确定设备类型。某些 BIOS 也可以配置为在其设置中强制使用特定的设备类型。

下一步是确定设备是否可启动。这意味着至少要读取驱动器的第一个扇区,因为如果无法读取它就无法启动。然后 BIOS 可能会尝试确定引导扇区是否有效。如果它正在模拟硬盘驱动器,它应该始终检查是否存在 0xAA55 签名,但如果它正在模拟软盘,它可能会也可能不会执行此检查。如果它正在模拟一个硬盘驱动器,它可能还会检查一个有效的 MBR 式分区,如果它正在模拟一个软盘,它可能会检查一个有效的 FAT BIOS 参数块 (BPB)。

因此,至少在某些情况下,引导扇区末尾没有 0xAA55 幻数会阻止 USB 设备被用作引导设备。然而,BIOS 很可能会选择为您的设备使用软盘模拟,并且许多 BIOS 除了验证其可读性之外不会对引导扇区进行任何验证。但是,如果您可以让您的 USB 设备假装它比实际大得多,超出软盘仿真支持的范围,那么它应该更有可能使用硬盘驱动器仿真。

正在调用 INT 0x18

然而,即使 BIOS 加载并执行设备的引导扇区,也不会丢失所有内容。您可以调用 INT 0x18,它最初用于在原始 IBM PC 上启动盒式 BASIC,但被 BIOS 引导规范重新定义为失败引导尝试的恢复向量。在现代 PC 上,包括任何支持启动 USB 设备的 PC,调用它会告诉 BIOS 尝试下一个启动设备。 (INT 0x18 实际上是在标准 MBR 实现启动失败后调用的,因为 MBR 被发明了,所以它实际上并不像听起来那么不寻常。)

这是调用 INT 0x18 的引导扇区的源代码,我用于在可用的各种 PC 上进行测试:

    .code16

    .text

    jmp start
    nop
    .org    0x03    # insure a two byte long JMP insn was used

    .org    0x3e    # skip over FAT BIOS Parameter Block
start:
    xor %ax, %ax
    mov %ax, %ss
    mov [=10=]x7c00, %sp

    call    print_message

    .ascii  "\r\nOOPS... The BIOS tried to boot the custom device.\r\n"
    .byte   0

    xor %ax, %ax
    int [=10=]x16       # read from keyboard

    # Try using the BIOS Boot Specification's recovery vector so
    # that BIOS tries another boot device. On an a real 5150 IBM PC
    # this starts cassette BASIC.  On a PC that doesn't support the
    # BBS then it may print an error message, do nothing or crash.

    int [=10=]x18

    call    print_message

    # INT 0x18 didn't work, didn't start BASIC and didn't crash.
    # Print an error message to user and invoke the INT 0x19
    # reboot vector.

    .ascii  "\r\nRemove the custom device and press any key to reboot"
    .ascii  " the computer.\r\n"
    .byte   0

    xor %ax, %ax
    int [=10=]x16       # read from keyboard

    int [=10=]x19       # reboot

infinite_loop:
    jmp infinite_loop

print_message:
    pop %si
loop_message:
    lods    %cs:(%si), %al
    test    %al,%al
    jz  end_message
    mov [=10=]xe, %ah
    mov [=10=]x7, %bx
    int [=10=]x10       # print a character
    jmp loop_message
end_message:
    jmp *%si

    .org    0x1FF
    .byte   00      # note no 55 aa signature

您应该能够修改此代码以用于您的设备。确保从设备的 BPB 复制 FAT12 BPB(偏移量 0x003 和 0x03e 之间的字节)。您可能希望删除 INT 0x16 的第一次调用,它在调用 INT 0x18 之前等待按键。要 assemble 和 link 您可以使用以下命令的代码:

as -o myfat12.o myfat12.s
ld --oformat=binary -o myfat12.bin -Ttext=0x7c00 myfat12.o

要将 BPB 从您的设备复制到使用上述命令创建的引导扇区,您可以使用类似的东西:

dd conv=notrunc if=/dev/sda of=myfat12.bin bs=1 skip=3 seek=3 count=59

在我的测试中,上面的代码运行良好,但在一种情况下,BIOS 只是一遍又一遍地尝试相同的引导设备。在另一种情况下,PC(一台 15 岁的 Pentium 4)在尝试从 USB 闪存驱动器启动代码时崩溃,但不是从实际的 USB 软盘启动。崩溃可能与我用来尝试尽可能接近地复制您的设备的不寻常的 24k FAT12 BPB 有关。

第二条 "Remove the custom device..." 消息在我的测试中从未打印出来。引导块从一开始就没有执行,或者调用 0x18 做了一些事情但从未返回。您可能希望将这些说明移到第一条消息中,以防 INT 0x18 没有任何帮助。