nasm : 做自己的MBR 从定制的扇区索引加载连续的扇区

来源:互联网 发布:鼠标知乎 编辑:程序博客网 时间:2024/05/22 04:39

前言

在正式做 boot loader之前,我要解决16位汇编代码空间不足的问题.

逆向ultraIso制作的可用MBR后,看到她在MBR代码, 将自己的MBR代码拷贝0x600, 然后加载1#扇区到0x7c00. 然后跳到0x7c00执行BOOT代码.


我做了下修改, 方便自己做实验.

先手工在U盘中找到一片连续扇区,开始索引UB, 连续扇区长度UL, 字节长度25KB

可以确定, 内存中的0x7e00~0xe000, 是不被BIOS使用的.

MBR代码由BIOS加载到0x7c00后, 从自己找到的UB开始, 从U盘加载此连续扇区到内存0x7e00, 加载的字节长度和找到的连续物理扇区长度(25KB)相同.

然后跳到0x7e00开始执行自己的BOOT代码.

这样,我就有了25KB二进制的16位汇编代码,可以用来做boot loader实验.

每个实验的代码,都能在一个BOOT代码中完成.

即使用NASM写的再挫,也不可能像写MBR代码那样,出现扇区空间不够用的问题~


已经实验过了, (int 13h , ah = 42h) 和 (int 13h , ah = 2h) 都可以加载连续连续扇区到内存.

在程序执行完成后,准备跳到BOOT代码时,用bochs调试命令,看过了已经加载好的内存空间,和U盘上找到的那片物理磁盘扇区上的内容相同.

代码预览

; /// @file ls-boot-loader/mbr.asm; /// @brief 我们自己的MBR, 加上可以更换的分区表信息, 载入我们自己的连续扇区到内存; /// @note 编译命令行 ; /// cd D:\prj\nasm_prj\boot\ls-boot-loader; /// d:; ///C:\nasm\nasm.exe mbr.asm -o mbr.bin -l mbr.list; /// @note 将 mbr.bin 写到U盘0扇区; ///MBR代码空间很紧张, 不是"一定需要堆栈平衡的call", 可以不进行call之后的堆栈平衡操作, 等跳到BOOT代码, 再重新设置栈顶; /// 标识符命名约定; /// label_x 为标签, 供跳转指令使用; /// addr_x 为地址, 供内存操作指令使用; /// byte_x word_x 为全局变量, 供程序流程内使用, 在使用前需要初始化; /// fn_x 为函数入口地址, 供 call 指令使用; /// XXX 为宏, 必须全大写, 单词之间用'_'连接; /// 宏命名约定; /// SWITCH_X 编译用的开关, 发行前, 必须注释掉; /// MEMORY_ADDR_X 内存地址; /// SECTOR_INDEX_X 扇区索引; /// SECTOR_BLOCK_SIZE_ 扇区索引对应的连续扇区块数; /// CNT_X 数量; /// BYTE_FLAG_X 特定标志的值, e.g. 激活 = 0x80; /// LEN_X 长度; /// BIT_VALID_X 位标志字节值定义; /// STACK_IN_PARAM_X 入参, 以bp为参照点; /// STACK_OUT_PARAM_X 出参, 以si为参照点;--------------------------------------------------------------------------------; /// 宏定义;--------------------------------------------------------------------------------; /// 如果没定义 SWITCH_USE_CHS_READ_SECTOR_TO_MEMORY, 就优先执行 int 13h ah = 42h, 扩展读扇区到内存, ; /// 如果失败, 才执行int 13h ah = 2h, 根据CHS读扇区到内存; /// 等调试完成, 请注释掉 SWITCH_USE_CHS_READ_SECTOR_TO_MEMORY; %define SWITCH_USE_CHS_READ_SECTOR_TO_MEMORY ; ///< 使用 int 13h ah = 2h 的CHS接口读扇区到内存%define SWITCH_WHEN_MBR_END_LOOP_DEAD ; ///< MBR程序结束后, 不跳到已经载入到内存的扇区, 在本地死循环, 用于调试MEMORY_ADDR_BOOT_CODE_ORG_ENTRY equ 0x7c00 ; ///< MBR程序入口地址MEMORY_ADDDR_LOADER_MODULE equ 0x7e00; ///< "载入"模块地址; /// 在实验U盘上,找到的没有被使用的U盘连续扇区索引范围(14~3229, 3231~17999); /// 实验过程 : http://blog.csdn.net/lostspeed/article/details/48804455; /// 14~3229扇区足够我们做实验了; /// 0x9f000以上是 Extended BIOS data Area(1~4K, usually 1K), 最多只能到0x9ee00开始, 0x9ee00 ~ 0x9f000; /// int 13h, ah = 42h时,能读的扇区数最大为127(0x7f)个; /// 02h    WORD    number of blocks to transfer (max 007Fh for Phoenix EDD); /// int 13h, ah = 2h时, 能读取的扇区最大值,没有特别说明,为了兼容 ah = 42h, 将BOOT连续扇区最大块数最大可能定为127; /// 一个段size = 64KB = 0xffff, 有128个扇区SECTOR_BLOCK_SIZE_MAX_BY_BIOS_COPY equ ((0xFE00 - MEMORY_ADDDR_LOADER_MODULE) / 0x200) ; ///< 用BIOS接口一次能拷贝的最大扇区块数(64)DISK_SECTOR_INDEX_BOOT_CODE equ 14 ; ///< 在物理磁盘上, BOOT代码所在的起始扇区SECTOR_BLOCK_SIZE_BOOT_CODE_SEG_LIMIT equ (SECTOR_BLOCK_SIZE_MAX_BY_BIOS_COPY - DISK_SECTOR_INDEX_BOOT_CODE) ; ///< BOOT代码在0段的连续扇区最大块数(50), 可容纳25600字节(25KB二进制代码)DISK_SECTOR_INDEX_BOOT_CODE_LAST equ (DISK_SECTOR_INDEX_BOOT_CODE + SECTOR_BLOCK_SIZE_BOOT_CODE_SEG_LIMIT - 1) ; ///< 在物理磁盘上, BOOT代码所在的结束扇区, 63; /// 0x7e00MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST equ MEMORY_ADDDR_LOADER_MODULE ; ///< 新扇区要拷贝到的内存地址; /// 0xe000MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST_LAST equ (MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST + 512 * (SECTOR_BLOCK_SIZE_BOOT_CODE_SEG_LIMIT - 1)); ///< 新扇区要拷贝到的内存地址最后一个扇区的起始地址; /// 0x1c00DISK_ADDR_SECTOR_NEED_COPY_TO_DEST equ (512 * 14); ///< 新扇区要拷贝的磁盘地址开始扇区的起始地址; /// 0x7e00DISK_ADDR_SECTOR_NEED_COPY_TO_DEST_LAST equ (512 * (14 + SECTOR_BLOCK_SIZE_BOOT_CODE_SEG_LIMIT - 1)); ///< 新扇区要拷贝的磁盘地址结束扇区的起始地址MEMORY_ADDR_STACK_TOP equ 0x7c00 ; ///< 栈顶地址CNT_PARTITION_ENTRY equ 4 ; ///< 分区表入口总数量BYTE_FLAG_PARTITION_ACTIVE equ 0x80 ; ///< 分区激活标志字节内容LEN_PARTITION_ENTRY equ 0x10 ; ///< 分区表入口长度为16Bytes; /// byte_status字节状态位定义; /// 状态位 : 磁盘安装检查(DISK INSTALLATION CHECK). 1 = 已安装, 0 = 未安装BIT_VALID_DISK_INSTALLATION_CHECK_SET equ 00000001b ; ///< 设置DISK_INSTALLATION_CHECK-有效位标志, 用OR方法, TEST位时,也用这个值BIT_VALID_DISK_INSTALLATION_CHECK_CLR equ 11111110b ; ///< 清除DISK_INSTALLATION_CHECK-有效位标志, 用AND方法BIT_VALID_MAX_CHS_SET equ 00000010b ; ///< 已经得到磁盘的最大CHS-有效位标志, 用OR方法, TEST位时,也用这个值BIT_VALID_MAX_CHS_CLR equ 11111101b ; ///< 已经得到磁盘的最大CHS-有效位标志, 用AND方法; 栈入参 - 以函数入口处的bp为基准%define STACK_IN_PARAM_1 [bp + 2]%define STACK_IN_PARAM_2 [bp + 4]%define STACK_IN_PARAM_3 [bp + 6]%define STACK_IN_PARAM_4 [bp + 8]%define STACK_IN_PARAM_5 [bp + 10]%define STACK_IN_PARAM_6 [bp + 12]%define STACK_IN_PARAM_7 [bp + 14]%define STACK_IN_PARAM_8 [bp + 16]%define STACK_IN_PARAM_9 [bp + 18]; 栈出参 - 以函数入口处的sp为基准, 函数出口处的sp必须和入口处相同; 必须先执行 mov si, sp 因为没有 mov ax, [sp + 4] 这样的指令; 用完 STACK_OUT_PARAM_X 之后, 执行 pop bp%define STACK_OUT_PARAM_1 [si + 0]%define STACK_OUT_PARAM_2 [si + 2]%define STACK_OUT_PARAM_3 [si + 4]%define STACK_OUT_PARAM_4 [si + 6]%define STACK_OUT_PARAM_5 [si + 8]%define STACK_OUT_PARAM_6 [si + 10]%define STACK_OUT_PARAM_7 [si + 12]%define STACK_OUT_PARAM_8 [si + 14]%define STACK_OUT_PARAM_9 [si + 16]bits 16 ; ///< 16位汇编;--------------------------------------------------------------------------------; /// MBR程序入口;--------------------------------------------------------------------------------org MEMORY_ADDR_BOOT_CODE_ORG_ENTRYcli ; ///< 屏蔽中断xor ax, axmov ds, axmov es, axmov ss, axmov sp, MEMORY_ADDR_STACK_TOP; /// 全局变量初始化mov byte[byte_status], al ; ///< 状态字节,初始化为FALSEmov byte[byte_udisk_sn], al; mov word[word_disk_maximum_cylinder_number], axmov word[word_disk_maximum_head_number], axmov word[word_disk_maximum_sector_number], axsti ; ///< 响应中断cld ; ///< 清方向标志位, 使设计到si,di的操作后, si++, di++; /// 直接在0x7c00操作, 不拷贝到其他扇区再操作, 我们拷贝U盘1#扇区到MEMORY_ADDDR_LOADER_MODULEmov [byte_udisk_sn], dl ; ///< 保存U盘号码0x80; /// 显示信息 - 从U盘启动mov si, str_start_boot_from_usb_devicecall fn_disp_str; /// 校验分区表-查找有效的分区表入口mov si, addr_partition_tablexor ax, axmov cx, CNT_PARTITION_ENTRY ; 分区入口(partition entry)共4条label_verify_partition_entry_do_once:test byte [si], BYTE_FLAG_PARTITION_ACTIVE ; 判断该分区入口是否为激活jz label_verify_partition_entry_next ; ///< 如果分区表入口无效, 找下一个分区表入口inc ax ; ///< 保存-有效分区入口数量; /// bp为最后一个有效分区入口, 因为4个分区入口只有一个是激活的; /// 所以 bp 中保存的是唯一个被激活的分区入口mov bp, silabel_verify_partition_entry_next:add si, LEN_PARTITION_ENTRY ; ///< 切到下一个分区表入口loop label_verify_partition_entry_do_once ; ///< 判断该分区入口是否为激活dec axjz label_proc_valid_partition_table ; ///< 如果激活的分区表入口数量为1, 转有效处理; /// 如果分区表入口校验无效(没有唯一激活的分区表入口), 就不搞了, 报错(无效分区表)后,死循环mov si, str_invalid_partition_tablejmp label_show_msg_and_loop_dead;--------------------------------------------------------------------------------; /// 校验分区表;--------------------------------------------------------------------------------label_proc_valid_partition_table:; IBM/MS INT 13 Extensions - INSTALLATION CHECK; ; Parameter input; AH = 41h; BX = 55AAh; DL = drive (80h-FFh);; Parameter output;; CF set on error (extensions not supported); AH = 01h (invalid function);; CF clear if successful; BX = AA55h if installed; AH = major version of extensions; 01h = 1.x; 20h = 2.0 / EDD-1.0; 21h = 2.1 / EDD-1.1; 30h = EDD-3.0; AL = internal use; CX = API subset support bitmap (see #00271); DH = extension version (v2.0+ ??? -- not present in 1.x);; Bitfields for IBM/MS INT 13 Extensions API support bitmap:;; Bit(s)  Description     (Table 00271); 0      extended disk access functions (AH=42h-44h,47h,48h) supported; 1      removable drive controller functions (AH=45h,46h,48h,49h,INT 15/AH=52h); supported; 2      enhanced disk drive (EDD) functions (AH=48h,AH=4Eh) supported.; Extended drive parameter table is valid (see #00273,#00278); 3-15   reserved (0)mov dl, [byte_udisk_sn]     ; restore dl from bk_byte_dlmov ah, 41hmov bx, 55AAhint 13hjb short not_support_int_13h_extensions ; CF clear if successfulcmp bx, 0AA55h      ; BX = AA55h if installedjnz not_support_int_13h_extensions; /// CX = API subset support bitmap (see #00271)test cl, 1 ; 0      extended disk access functions (AH=42h-44h,47h,48h) supportedjz not_support_int_13h_extensions; /// 如果支持 int13h 磁盘扩展, 做标志, 跳到 用磁盘扩展方法去读扇区到内存; /// 不用动态修改代码的方式,太不正规了. 如果做补丁,可以这样动态修改代码.%ifdef SWITCH_USE_CHS_READ_SECTOR_TO_MEMORYjmp not_support_int_13h_extensions%else; /// 设置有效位标志-DISK_INSTALLATION_CHECKmov al, [byte_status];or al , BIT_VALID_DISK_INSTALLATION_CHECK_SETmov byte[byte_status], aljmp label_read_sectors_into_memory%endif;--------------------------------------------------------------------------------not_support_int_13h_extensions:; int 13h, ah = 08h;; parameter IN;; AH = 08h; DL = drive (bit 7 set for hard disk); ES:DI = 0000h:0000h to guard against BIOS bugs;; parameter OUT;; CF set on error; AH = status (07h) (see #00234); ; CF clear if successful; AH = 00h; AL = 00h on at least some BIOSes; BL = drive type (AT/PS2 floppies only) (see #00242); CH = low eight bits of maximum cylinder number; CL = maximum sector number (bits 5-0); high two bits of maximum cylinder number (bits 7-6); DH = maximum head number; DL = number of drives; ES:DI -> drive parameter table (floppies only)mov dl, [byte_udisk_sn]mov di, 0 ; ///< ES:DI = 0000h:0000h to guard against BIOS bugsmov ah, 8stcint 13h; 先保存得到的MaxCHS, 不管对错, 后面判断完后,会设置有效位标志-已经得到磁盘的最大CHS; 已经得到了CHS的最大值, 保存pushfpusha; CH = low eight bits of maximum cylinder number; CL = maximum sector number (bits 5-0); high two bits of maximum cylinder number (bits 7-6)mov ax, cxshr al, 6xchg ah, almov [word_disk_maximum_cylinder_number], ax; DH = maximum head numberxor ax, axmov al, dhmov word[word_disk_maximum_head_number], ax; CL = maximum sector number (bits 5-0)mov ax, cxand ax, 0x3fmov word[word_disk_maximum_sector_number], ax; /// 清除有效位标志-已经得到磁盘的最大CHS; /// 因为现在还不知道得到的值正确与否; /// 现在保存,是为了在第一现场保存, 实现起来简单可靠; /// 后面判断时, 将寄存器的值都改变了mov al, [byte_status];and al , BIT_VALID_MAX_CHS_CLRmov byte[byte_status], alpopapopf;<bochs:74> r;rax: 00000000_00000000 rcx: 00000000_0009aeff;rdx: 00000000_0000fe01 rbx: 00000000_0000aa55;rsp: 00000000_00007bf0 rbp: 00000000_00007dbe;rsi: 00000000_000e7dfe rdi: 00000000_00000000;r8 : 00000000_00000000 r9 : 00000000_00000000;r10: 00000000_00000000 r11: 00000000_00000000;r12: 00000000_00000000 r13: 00000000_00000000;r14: 00000000_00000000 r15: 00000000_00000000;rip: 00000000_00007c73;eflags 0x00000202: id vip vif ac vm rf nt IOPL=0 of df IF tf sf zf af pf cfjb label_cant_get_chs ; ///< CF clear if successfuland ah, ah ; ///< AH = 00h is okjnz label_cant_get_chs ; ///< ah must be 0x00mov al, dh ; ///< maximum head number, now dh = 0xfe = 254inc aljz label_cant_get_chs ; ///< maximum head number is 255, head number range(1 ~ 255); /// now ax = 0ffand cx, 3Fh ; /// < now cx = 0xaeffjz label_cant_get_chs ; ///< sector number must > 0, now sector number = 0x3f = 63jmp label_save_chs; /// 原版在执行完(int 13h, ah = 8)后,有逻辑错误; /// 如果查询CHS失败, 就无法继续强制执行了; /// 因为得不到CHS, 按照CHS读取磁盘扇区到内存,一定会失败的...label_cant_get_chs:mov si, str_error_cant_get_chsjmp label_show_msg_and_loop_deadlabel_save_chs:; /// 设置有效位标志-已经得到磁盘的最大CHSmov al, [byte_status];or al , BIT_VALID_MAX_CHS_SETmov byte[byte_status], allabel_read_sectors_into_memory:push SECTOR_BLOCK_SIZE_BOOT_CODE_SEG_LIMITpush DISK_SECTOR_INDEX_BOOT_CODEpush MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST; /// @fn fn_read_sectors_into_memory(WORD wDstAddrCopyTo, WORD wSrcSectorIndex, WORD wSrcSectorBlockCnt)call fn_read_sectors_into_memoryadd sp, (3 * 2) ; ///< 堆栈平衡jnb ok_proc_read_sectors_into_memory ; addr_word_boot_signatureerr_loading_operating_system:mov si, str_error_loading_operating_system ; show str_error_loading_operating_systemjmp label_show_msg_and_loop_dead;--------------------------------------------------------------------------------ok_proc_read_sectors_into_memory:mov ax, [ds:7DFEh]    ; addr_word_boot_signaturecmp ax, 0AA55hjz ok_boot_signaturejmp err_loading_operating_system ; str_error_loading_operating_system;--------------------------------------------------------------------------------ok_boot_signature:cli; /// 显示信息, MBR程序已经运行okmov si, str_mbr_code_execute_overcall fn_disp_str; /// only for debug, addr bgin and addr end; mov ax, MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST; mov bx, MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST_LAST; mov cx, DISK_ADDR_SECTOR_NEED_COPY_TO_DEST; mov dx, DISK_ADDR_SECTOR_NEED_COPY_TO_DEST_LAST%ifdef SWITCH_WHEN_MBR_END_LOOP_DEADjmp $%else; /// 准备跳进已经载入的第一个新扇区, 这些新扇区在U盘上和内存中都是连续的jmp MEMORY_ADDR_SECTOR_NEED_COPY_TO_DEST%endiflabel_show_msg_and_loop_dead:call fn_disp_strjmp $; =============== S U B R O U T I N E =======================================fn_disp_str:lodsband al, aljz fn_disp_str_endmov ah, 0Ehmov bx, 7int 10h             ; - VIDEO - WRITE CHARACTER AND ADVANCE CURSOR (TTY WRITE); AL = character, BH = display page (alpha modes); BL = foreground color (graphics modes)jmp fn_disp_strfn_disp_str_end:ret; =============== S U B R O U T I N E =======================================; /// @fn fn_read_sectors_into_memory(WORD wDstAddrCopyTo, WORD wSrcSectorIndex, WORD wSrcSectorBlockCnt); /// @brief 读扇区到内存; /// @param WORD wDstAddrCopyTo (读磁盘扇区到内存时的)目标内存地址; /// @param WORD wSrcSectorIndex (读磁盘扇区到内存时的)源扇区起始索引; /// @param WORD wSrcSectorBlockCnt (读磁盘扇区到内存时的)源扇区块数; /// 函数要ret, 必须保持堆栈平衡! 否则返回时,不是调用此函数汇编命令的下一个字节地址fn_read_sectors_into_memory:; /// 保护现场 bp和sp push bp push sp; /// 使bp回到函数入口处的值push ax mov ax, sp add ax, 6 mov bp, axpop axpusha ; ///< => 函数入口保护所有普通寄存器; /// 读扇区之前, 将磁盘重置 ;DISK - RESET DISK SYSTEM; Note: Forces controller to recalibrate drive heads (seek to track 0);AH = 00h;DL = drive (if bit 7 is set both hard disks and floppy disks reset);Return:;AH = status (see #00234);CF clear if successful (returned AH=00h);CF set on errorxor ah, ahmov dl, [byte_udisk_sn]int 13hjb fn_read_sectors_into_memory_end ; CF clear if successful ; ///< pb 0x7cfc; /// 根据磁盘安装检查的结果; /// 选择使用int 13h ah = 42h 扩展读扇区; /// 还是使用int 13h ah = 2h 根据CHS来读扇区test byte[byte_status], BIT_VALID_DISK_INSTALLATION_CHECK_SETjz label_read_disk_to_memory_by_chs ; ///< 不是TRUE(磁盘安装检查无效), 转 int13h, ah = 2 读扇区; /// 磁盘安装检查有效时, 使用磁盘扩展读功能, 读扇区到内存; /// 使用 int13h, ah = 0x42 读扇区;Format of disk address packet:;Offset  Size    Description     (Table 00272);10h    QWORD   (EDD-3.0, optional) 64-bit flat address of transfer buffer;;used if DWORD at 04h is FFFFh:FFFFh;08h    QWORD   starting absolute block number;(for non-LBA devices, compute as;(Cylinder*NumHeads + SelectedHead) * SectorPerTrack +;SelectedSector - 1;04h    DWORD   -> transfer buffer;02h    WORD    number of blocks to transfer (max 007Fh for Phoenix EDD);01h    BYTE    reserved (0);;00h    BYTE    size of packet (10h or 18h); /// 压入 disk address packet, 并将栈顶给si,供 int 13h, ah = 0x42 使用; /// 这么搞确实省空间,不过很容易填错参数, 分析者也容易看错参数; fn_read_sectors_into_memory(WORD wDstAddrCopyTo, WORD wSrcSectorIndex, WORD wSrcSectorBlockCnt);08h    QWORD   starting absolute block numberxor ax, axpush axpush axpush axmov ax, STACK_IN_PARAM_2push ax;04h    DWORD   -> transfer bufferxor ax, axpush axmov ax, STACK_IN_PARAM_1push ax;02h    WORD    number of blocks to transfer (max 007Fh for Phoenix EDD)mov ax, STACK_IN_PARAM_3push axpush 0010h ;00h    BYTE    size of packet (10h or 18h)   ;01h    BYTE    reserved (0); ; /// DS:SI -> disk address packetmov si, spmov ah, 0x42mov dl, [byte_udisk_sn]     ; DL = driveint 13h             ; DISK - READ SECTORS INTO MEMORY; /// 这里add ap, 不会引起 CF位变化, 因为高位是栈顶, 恢复的栈指针偏移不到(0x7c00 - 0x6fff); /// 所以不用考虑保存CF的事情add sp, (8 * 2) ; ///< 堆栈平衡jmp fn_read_sectors_into_memory_after; /// 磁盘安装检查无效时,根据MaxCHS读磁盘到内存label_read_disk_to_memory_by_chs:xor ax, axpush ax ; ///< STACK_OUT_PARAM_4, sectorpush ax ; ///< STACK_OUT_PARAM_3, headpush ax ; ///< STACK_OUT_PARAM_2, trackmov ax, STACK_IN_PARAM_2 ; ///< (WORD wDstAddrCopyTo, WORD wSrcSectorIndex, WORD wSrcSectorBlockCnt)push axcall fn_LBA_2_CHS ; ///< fn_LBA_2_CHS(wSrcSectorIndex, track, head, sector); AH = 02h; AL = number of sectors to read (must be nonzero); CH = low eight bits of cylinder number; CL = sector number 1-63 (bits 0-5); high two bits of cylinder (bits 6-7, hard disk only); DH = head number; DL = drive number (bit 7 set for hard disk); ES:BX -> data buffer; ; Return:; ; CF set on error; if AH = 11h (corrected ECC error), AL = burst length; ; CF clear if successful; AH = status (see #00234); AL = number of sectors transferred (only valid if CF set for some; BIOSes); /// fn_read_sectors_into_memory(WORD wDstAddrCopyTo, WORD wSrcSectorIndex, WORD wSrcSectorBlockCnt); /// fn_LBA_2_CHS(wSrcSectorIndex, track, head, sector)mov si, sp ; ///< !mov ax, STACK_OUT_PARAM_2mov ch, al          ; CH = trackmov ax, STACK_OUT_PARAM_4mov cl, al          ; CL = sectormov ax, STACK_OUT_PARAM_3mov dh, al          ; DH = headmov bx, STACK_IN_PARAM_1mov dl, [byte_udisk_sn] ; DL = drive number (bit 7 set for hard disk)mov ax , STACK_IN_PARAM_3 ; AL = number of sectors to read (must be nonzero)mov ah, 2h ; AH = 02h; /// 因为要用到出参(call fn_LBA_2_CHS), 所以在这里堆栈平衡(call fn_LBA_2_CHS)add sp, (4 * 2) ; ///< 必须要堆栈平衡(call fn_LBA_2_CHS), 否则函数返回时, 地址不对了int 13h             ; DISK - READ SECTORS INTO MEMORYfn_read_sectors_into_memory_after:jb fn_read_sectors_into_memory_end ; ///< pb 0x7d51cmp ah, 0x00 ; ///< AH = status (see #00234), 00h = successful completionjnz fn_read_sectors_into_memory_endclc ; ///< 清除进位标记,代表成功fn_read_sectors_into_memory_end:popa ; ///< <= 函数入口保护所有普通寄存器; /// 恢复现场 bp和sp pop sp pop bpret; /// fn_LBA_2_CHS(WORD IN wSrcSectorIndex, WORD OUT track, WORD OUT head, WORD OUT sector)fn_LBA_2_CHS:; /// 保护现场 bp和sp push bp push sp; /// 使bp回到函数入口处的值push ax mov ax, sp add ax, 6 mov bp, axpop ax; /// LBA to CHS or CHS to LBA 的算法资料; /// 参照 http://blog.csdn.net/lostspeed/article/details/48895975; /// C, H and S are the cylinder number, the head number, and the sector number; /// LBA is the logical block address; /// HPC is the maximum number of heads per cylinder (reported by disk drive, typically 16 for 28-bit LBA); /// SPT is the maximum number of sectors per track (reported by disk drive, typically 63 for 28-bit LBA); /// C = LBA ÷ (HPC × SPT); /// H = (LBA ÷ SPT) mod HPC; /// S = (LBA mod SPT) + 1; /// 已经保存好的MaxCHS; /// word_disk_maximum_cylinder_number 磁盘最大柱面数; /// word_disk_maximum_head_number 磁盘最大头数; /// word_disk_maximum_sector_number 磁盘最大扇区数; /// STACK_IN_PARAM_1 是 wSrcSectorIndex; /// STACK_IN_PARAM_2 是 track; /// STACK_IN_PARAM_3 是 head; /// STACK_IN_PARAM_4 是 sector; /// DIV r/m8, AL ← Quotient, AH ← Remainde; /// 根据上述参考资料, 整理计算出参CHS的公式; /// STACK_IN_PARAM_2 = STACK_IN_PARAM_1 ÷ word_disk_maximum_sector_number ÷ word_disk_maximum_head_number; /// STACK_IN_PARAM_4 = (STACK_IN_PARAM_1 mod word_disk_maximum_sector_number) + 1; /// STACK_IN_PARAM_3 = (STACK_IN_PARAM_1 ÷ word_disk_maximum_sector_number) mod word_disk_maximum_head_numbermov ax, STACK_IN_PARAM_1mov cl, [word_disk_maximum_sector_number]div cl ; al = STACK_IN_PARAM_1 ÷ word_disk_maximum_sector_number, ah = STACK_IN_PARAM_1 mod word_disk_maximum_sector_numberinc ahmov STACK_IN_PARAM_4, ahxor ah, ahmov cl, [word_disk_maximum_head_number]div clmov STACK_IN_PARAM_2, almov STACK_IN_PARAM_3, ah; /// 恢复现场 bp和sp pop sp pop bpret;------------------------------------------------; 宏: LBA_TO_CHS; 描述:;       参数 1: 物理 sector 号;-----------------------------------------------;%macro LBA_TO_CHS 1;        mov ax, %1                      ; paramter;        mov cl, SPT;        div cl                          ; al = LBA / SPT, ah = LBA % SPT        ; cylinder = LBA / SPT / HPC;        mov ch, al;        shr ch, (HPC / 2)               ; ch = cylinder                ; head = (LBA / SPT ) % HPC;        mov dh, al;        and dh, 1                       ; dh = head; sector = LBA % SPT + 1;        mov cl, ah;        inc cl                          ; cl = sector;%endmacro;--------------------------------------------------------------------------------; /// 字符串定义;--------------------------------------------------------------------------------str_start_boot_from_usb_device:; db 0x0d, 0x0a, "start boot from usb device", 0x0d, 0x0a, 0db 0x0d, 0x0a, 'mb ', 0str_mbr_code_execute_over:; db "mbr code execute over", 0x0d, 0x0a, 0db 'me ', 0str_invalid_partition_table:; db 'error : Invalid partition table', 0x0d, 0x0a, 0db 'e1 ', 0str_error_loading_operating_system:; db 'error loading operating system', 0x0d, 0x0a, 0db 'e2 ', 0str_error_cant_get_chs:; db 'error cant get CHS', 0x0d, 0x0a, 0db 'e3 ', 0;--------------------------------------------------------------------------------; /// 临时变量;--------------------------------------------------------------------------------; /// 状态字节, 每一位代表一个状态; /// bit 0 分区是否有效, 1 = 分区有效, 0 = 分区无效byte_status:db 0xcdbyte_udisk_sn: ; ///< 进入MBR代码时, DL中存放的是硬盘号码, 0x80代表第一块硬盘(即U盘)db 0xcd; /// int 13h, ah = 8h 时,取到的磁盘最大柱面数word_disk_maximum_cylinder_number:dw 0xcdcd; /// int 13h, ah = 8h 时,取到的磁盘最大头数; /// HPC is the maximum number of heads per cylinder (reported by disk drive, typically 16 for 28-bit LBA)word_disk_maximum_head_number:dw 0xcdcd; /// int 13h, ah = 8h 时,取到的磁盘最大扇区数; /// SPT is the maximum number of sectors per track (reported by disk drive, typically 63 for 28-bit LBA)word_disk_maximum_sector_number:dw 0xcdcd;--------------------------------------------------------------------------------; 填充区;--------------------------------------------------------------------------------times 512 - ($ - $$) - 2 - (16 * 4) db 0; /// 对于不同的U盘分区size, 这里的值是不同的, partition_entry = 16, 共4个; /// 所以,使用不同的U盘做实验时, 格式化U盘后, 需要将U盘主分区的分区表入口抄下来,设置成下面的4个分区表入口; /// 这样才能将这512字节写入U盘0#扇区, 作为可用的MBR代码; /// 逆向UltraIos格式化U盘,写入的MBR代码, 可以看到代码中对此主分区分区表入口表, 做了校验.; /// 如果通不过校验, 不会加载后续的扇区到内存, 会跳到出错处理, 或走硬盘上的引导代码; /// 做实验时, 不能动格式化程序作出的扇区内容. 否则, 在Windows中插入U盘时, 会弹出提示"是否格式化此U盘"; /// 我们需要找出可以做实验用的连续扇区, 参照 http://blog.csdn.net/lostspeed/article/details/48804455; /// 在我们自己实现的MBR中, 要载入找到的连续可用扇区,载入内存, 用来做 boot loader 实验;Offset       0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F;0000001B0                                              80 00;0000001C0   02 00 0C FE FF AF 01 00  00 00 FF 3F E7 00;--------------------------------------------------------------------------------; /// Partition Table;--------------------------------------------------------------------------------addr_partition_table:db 0x80, 0x00, 0x02, 0x00, 0x0c, 0xfe, 0xff, 0xaf, 0x01, 0x00, 0x00, 0x00, 0xff, 0x3f, 0xe7, 0x00db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00;--------------------------------------------------------------------------------; /// Boot signature;--------------------------------------------------------------------------------addr_word_boot_signature:db 0x55, 0xaa

效果图

由于空间不够, 将显示信息已经做了最大程度的简化.  做人真难啊...




0 0
原创粉丝点击