;====================================================================
;
; FlyingDragon OS Boot Sector FOR NTFS File System
;
; Author: Jack
; V0.01 2005-9-1 20:58
;
; Build : nasm -f bin NTFS.ASM -oNTFS.BIN
;
;====================================================================================
;
; BIOS在启动中的角色:
; (1) BIOS装载引导驱动器上的0扇区(CHS = 0:0:1)内容到内存线性地址7C00H处;
; (2) BIOS检查所装载的扇区是否有启动标记(510、511字节分别为55H和AAH);
; (3) CPU寄存器DL被设置为分配给引导驱动器的驱动器号,00H为软驱A,80H为硬盘C;
; (4) BIOS跳转到其装载的扇区中的代码(即7C00H处),将控制权转交给引导代码。
;
; 引导代码应该初始化以下寄存器:
; (1) DS:某些BIOS设置其值为0,某些设置其为40H,它应该被设置为(7C00H-BOOT_ORG)/16;
; 其中,BOOT_ORG为引导代码的ORG值,该值通常为7C00H(这意味着DS应设置为0);
; (2) SS和SP(堆栈):这两个寄存器的初始值依赖于BIOS;
; (3) CS个IP(通过JMP指令):大多数的BIOS进入启动代码的地址为0000:7C00H,但是某些
; BIOS却跳转到07C0:0000H。由于短跳转和条件跳转是IP相关的,因此如果没有使用
; 远跳转或者绝对跳转,则不需要重置CS和IP;然而,DS仍旧必须是正确的值。
;
;=====================================================================================
;
; 常规内存( 0000 0000H - 000F FFFFH,即0-1MB )在系统启动时的使用情况
;
;=====================================================================================
;
; ---------------------------------
; | 0000 0000 - 0000 03FF | 1024B IDT read only
; |-------------------------------|
; | 0000 0400 - 0000 04FF | 256B BIOS Data Area , read only
; |-------------------------------|
; | 0000 0500 - 0000 7BFF |* 30464B Free Memory , read/write (29.75KB)
; |------------------------------ |
; | 0000 7C00 - 0000 7DFF | 512B Boot Sector , read/write
; |------------------------------ |
; | 0000 7E00 - 0000 7FFF | 512B Free Memory , read /write
; |------------------------------ |
; | 0000 8000 - 0009 FBFF | 607KB Free Memory , read / write( 32K - 639KB )
; |------------------------------ |
; | 0009 FC00 - 0009 FFFF |** 1KB EBDA extended BIOS data area
; |------------------------------ |
; | 000A 0000 - 000A FFFF | 64KB Video Memory
; |------------------------------ |
; | 000B 0000 - 000B 7FFF | 32KB Mono Video Text Memory
; |------------------------------ |
; | 000B 8000 - 000B FFFF | 32KB Color Video Text Memory
; |------------------------------ |
; | 000C 0000 - 000C 7FFF | 32KB Video BIOS , read only
; |------------------------------ |
; | 000C 8000 - 000E FFFF | 160KB Adapter ROM,read only
; |------------------------------ |
; | 000F 0000 - 000F FFFF | 64KB System BIOS, read only
; |------------------------------ |
; | 0010 0000 - 0010 FFEF |***64KB-16 High Memory Area,read/write ( 1MB开始处 )
; |------------------------------ |
; | 0010 FFF0 - | Free Extended Memory, read/write
; |------------------------------ |
;
; * 空闲内存实际并非从 0000 0500处开始,BIOS数据区实际上会利用从0000 0500开始的少量字节,例如
; 00000500处保存的是打印屏幕状态,当按下打印屏幕(PrintScreen)键时,低级键盘BIOS初始化打印屏
; 幕功能,键盘BIOS触发中断5打印屏幕处理程序。正因为BIOS数据区越过了256B的界限,因此DOS实际
; 上是从0000 0522开始装载的。为保险起见,可从0000 0600开始利用空闲内存。(1.5K - 31K 29.5KB)
;
; ** 有些机器上没有这段BIOS扩展数据区。
;
; *** 如果没有使用扩展高端内存区域程序(例如Emm386.exe),则从0010 0000 (1MB )开始的内存都是可用的。
;
;
;=====================================================================================
;
BITS 16 ; 生成16位代码而不是32位代码
SECTION .TEXT ; 代码段
ORG 0800H ; 指定程序被装入内存的起始位置
;====================================================================
;
; NTFS启动扇区代码使用内存的情况:
; 0000 0000 - 0000 07FF 2K IDT和BIOS数据
; 0000 0800 - 0000 2800 8K 保留给NTFS启动扇区代码,最多8K
;
; ****:**** - 0000 7FFF 22K 堆栈区域
; 0000:8000 - ****:**** 480K 装载第二阶段程序FDOSLDR.BIN及数据的空间
; 0008 0000 - 0008 FFFF 64K 用于文件系统的缓冲区
; 0009 0000 - 0009 FFFF 64K 用于读取数据簇的缓冲区
;
;====================================================================
;
; 宏和常量定义
;
;====================================================================
? EQU 0 ; NASM不支持DW ?这样的语法,可以使用这样的定义
; 模拟,以使代码的可读性更强
STACK_ADDR EQU 7FD0H ; 堆栈栈顶(注意:堆栈大小约为20K左右)
DATA_BUF_SEG EQU 9000H ; 用于读取根目录或文件内容的缓冲区(64K) 段地址
DATA_BUF_OFF EQU 0000H ; 数据缓冲区偏移
DATA_BUF_ADDR EQU 90000H ; 数据缓冲区线性地址
FILE_BUF_SEG EQU 8000H ; 文件记录缓冲区段地址
FILE_BUF_OFF EQU 00000H ; 文件记录缓冲区偏移
BOOT_SEC_NUM EQU 16 ; NTFS启动扇区代码的总长度(16个扇区=8K)
BOOT_SEC_ADDR EQU 0800H ; NTFS启动扇区的重定位地址
; 第二阶段装载程序FDOSLDR.BIN
OSLOADER_ADDR EQU 8000H ; FDOSLDR.BIN放入内存中的起始位置
OSLOADER_SEG EQU 0800H ; 起始段地址
;====================================================================
; 用堆栈保存若干中间变量( SS = 0 BP = 7C00H )
;====================================================================
DISK_EXT_SUPPORT EQU 1 ; BYTE 磁盘是否支持扩展BIOS
DRIVE_NUMBER EQU 2 ; BYTE 用于保存启动的磁盘驱动器号
BYTES_PER_FILE_RECORD EQU 8 ; DWORD 用于保存NTFS每个文件记录的尺寸
BYTES_PER_INDEX_BLOCK EQU 12 ; DWORD 用于保存默认的索引分配的尺寸
BYTES_PER_CLUSTER EQU 16 ; DWORD 用于保存每簇字节数字节数
;====================================================================
; 扩展磁盘服务所使用的地址包
;====================================================================
DAP_SECTOR_HIGH EQU 24 ; 起始扇区号的高32位 ( 每次调用需要重置 ) DWORD
DAP_SECTOR_LOW EQU 28 ; 起始扇区号的低32位 ( 每次调用需要重置 ) DWORD
DAP_BUFFER_SEG EQU 30 ; 缓冲区段地址 ( 每次调用需要重置 ) WORD
DAP_BUFFER_OFF EQU 32 ; 缓冲区偏移 ( 每次调用需要重置 ) WORD
DAP_RESERVED2 EQU 33 ; 保留字节
DAP_READ_SECTORS EQU 34 ; 要处理的扇区数(1 - 127 )
DAP_RESERVED1 EQU 35 ; 保留字节
DAP_PACKET_SIZE EQU 36 ; 包的大小为16字节
;====================================================================
; NTFS系统常量
;====================================================================
; 记录类型
NTFS_RECORD_TYPE_NONE EQU 0 ; 未知的类型
NTFS_RECORD_TYPE_FILE EQU 0x454C4946 ; 文件记录($MFT)
NTFS_RECORD_TYPE_INDX EQU 0x58444E49 ; 索引记录(Index Allocation)
NTFS_RECORD_TYPE_HOLE EQU 0x454C4F48 ; 空洞记录
NTFS_RECORD_TYPE_RSTR EQU 0x52545352 ; 重启记录($LogFile Restart Page )
NTFS_RECORD_TYPE_RCRD EQU 0x44524352 ; 日志记录($LogFile Log Record Page)
NTFS_RECORD_TYPE_CHKD EQU 0x424B4843 ; 检查记录($LogFile CHKDSK)
NTFS_RECORD_TYPE_BAAD EQU 0x44414142 ; 多扇区数据写入错误(通常是由于系统断电引起)
NTFS_RECORD_TYPE_FREE EQU 0xFFFFFFFF ; 记录是空闲的,在使用前必须初始化
;=============================================================
; NTFS系统文件记录编号
;=============================================================
NTFS_SYSTEM_FILE_MFT EQU 0 ; $MFT ( Master File Table )
NTFS_SYSTEM_FILE_MFTMIRR EQU 1 ; $MFTMirr ( 至少前四个MFT记录的拷贝)
NTFS_SYSTEM_FILE_LOGFILE EQU 2 ; $LogFile ( 事务日志)
NTFS_SYSTEM_FILE_VOLUME EQU 3 ; $Volume ( 卷名及卷信息以及文件系统版本 )
NTFS_SYSTEM_FILE_ATTRDEF EQU 4 ; $AttrDef ( 所有支持的属性定义 )
NTFS_SYSTEM_FILE_ROOT EQU 5 ; . ( 根目录 )
NTFS_SYSTEM_FILE_BITMAP EQU 6 ; $Bitmap ( 卷的数据簇分配位图 )
NTFS_SYSTEM_FILE_BOOT EQU 7 ; $Boot ( 卷的引导记录,指向引导扇区 )
NTFS_SYSTEM_FILE_BADCLUS EQU 8 ; $BadClus ( 卷的坏簇列表 )
NTFS_SYSTEM_FILE_SECURE EQU 9 ; $Secure ( 卷使用的安全描述符 )
NTFS_SYSTEM_FILE_UPCASE EQU 10 ; $UpCase ( 64K个UNICODE字符串的大写形式 )
NTFS_SYSTEM_FILE_EXTEND EQU 11 ; $Extend ( 包含其他系统文件的目录 $ObjId $Quota $Reparse $UsnJrnl - NTFS 3.0 )
NTFS_SYSTEM_FILE_RESERVED12 EQU 12 ; 保留
NTFS_SYSTEM_FILE_RESERVED13 EQU 13 ; 保留
NTFS_SYSTEM_FILE_RESERVED14 EQU 14 ; 保留
NTFS_SYSTEM_FILE_RESERVED15 EQU 15 ; 保留
NTFS_SYSTEM_FILE_FIRSTUSER EQU 16 ; 第一个用户可以使用的文件记录编号
; MFT文件记录属性
NTFS_FILE_RECORD_FLAG_INUSE EQU 0x0001 ; 正在使用
NTFS_FILE_RECORD_FLAG_DIRECTORY EQU 0x0002 ; 是目录
;==============================================================================
;
; 属性排序规则
;
;==============================================================================
NTFS_COLLATION_BINARY EQU 0 ; 按原始字节依次顺序比较
NTFS_COLLATION_FILE_NAME EQU 1 ; 按UNICODE方式比较文件名(不区分大小写?)
NTFS_COLLATION_UNICODE_STRING EQU 2 ; 按UNICODE字符串比较(区分大小写?)
NTFS_COLLATION_ULONG EQU 16 ; 按32为无符号整数排序
NTFS_COLLATION_SID EQU 17 ; 按SID值排序
NTFS_COLLATION_SECURITY_HASH EQU 18 ; 首先按哈希值排序,然后按SID排序
NTFS_COLLATION_ULONGS EQU 19 ; 按整数序列排序(GUID?)
;==============================================================================
;
; 属性定义标志(用于属性定义结构)
;
;==============================================================================
NTFS_ATTRDEF_FLAG_INDEXABLE EQU 0x00000002 ; 属性可以被索引
NTFS_ATTRDEF_FLAG_MULTIPLE EQU 0x00000004 ; 属性可以出现多次
NTFS_ATTRDEF_FLAG_NOT_NULLABLE EQU 0x00000008 ; 属性值必须至少包括一个非0字节
NTFS_ATTRDEF_FLAG_INDEXED_UNIQUE EQU 0x00000010 ; 属性必须被索引并且必须唯一
NTFS_ATTRDEF_FLAG_NAMED_UNIQUE EQU 0x00000020 ; 属性必须被命名并且名称必须唯一
NTFS_ATTRDEF_FLAG_RESIDENT EQU 0x00000040 ; 属性必须是驻留的
NTFS_ATTRDEF_FLAG_LOG EQU 0x00000080 ; 属性的修改必须记录日志,不管属性
; 是否为驻留属性;如果没有该标志,
; 则只记录驻留属性的修改日志
;==============================================================================
;
; 属性类型
;
;==============================================================================
; Type Name Flags IRN MinSize MaxSize
;------------------------------------------------------------------------------
; 0x10 $STANDARD_INFORMATION 0x40 R 0x30 0x48
; 0x20 $ATTRIBUTE_LIST 0x80 N - -
; 0x30 $FILE_NAME 0x42 IR 0x44 0x242
; 0x40 $VOLUME_VERSION 0x40 R 0x8 0x8
; 0x40 $OBJECT_ID 0x40 R - 0x100
; 0x50 $SECURITY_DESCRIPTOR 0x80 N - -
; 0x60 $VOLUME_NAME 0x40 R 0x2 0x100
; 0x70 $VOLUME_INFORMATION 0x40 R 0xC 0xC
; 0x80 $DATA 0x00 - -
; 0x90 $INDEX_ROOT 0x40 R - -
; 0xA0 $INDEX_ALLOCATION 0x80 N - -
; 0xB0 $BITMAP 0x80 N - -
; 0xC0 $SYMBOLIC_LINK 0x80 N - -
; 0xC0 $REPARSE_POINT 0x80 N - 0x4000
; 0xD0 $EA_INFORMATION 0x40 R 0x8 0x8
; 0xE0 $EA 0x00 - 0x10000
; 0xF0 $PROPERTY_SET - - - -
; 0x100$LOGGED_UTILITY_STREAM 0x80 N - 0x10000
;------------------------------------------------------------------------------
;
; 其中: I - Indexable
; N - NonNullable
; R - Resident
;
;==============================================================================
NTFS_ATTRIBUTE_TYPE_STANDARD_INFORMATION EQU 0x00000010 ; 标准信息
NTFS_ATTRIBUTE_TYPE_ATTRIBUTE_LIST EQU 0x00000020 ; 属性列表
NTFS_ATTRIBUTE_TYPE_FILE_NAME EQU 0x00000030 ; 文件名
NTFS_ATTRIBUTE_TYPE_VOLUME_VERSION EQU 0x00000040 ; 卷版本信息(WINNT)
NTFS_ATTRIBUTE_TYPE_OBJECT_ID EQU 0x00000040 ; 对象ID(Win2000/XP)
NTFS_ATTRIBUTE_TYPE_SECURITY_DESCRIPTOR EQU 0x00000050 ; 安全描述符
NTFS_ATTRIBUTE_TYPE_VOLUME_NAME EQU 0x00000060 ; 卷名称
NTFS_ATTRIBUTE_TYPE_VOLUME_INFORMATION EQU 0x00000070 ; 卷信息
NTFS_ATTRIBUTE_TYPE_DATA EQU 0x00000080 ; 数据
NTFS_ATTRIBUTE_TYPE_INDEX_ROOT EQU 0x00000090 ; 索引根目录
NTFS_ATTRIBUTE_TYPE_INDEX_ALLOCATION EQU 0x000000A0 ; 索引分配缓冲区
NTFS_ATTRIBUTE_TYPE_BITMAP EQU 0x000000B0 ; 位图
NTFS_ATTRIBUTE_TYPE_SYMBOLIC_LINK EQU 0x000000C0 ; 符号连接(WINNT)
NTFS_ATTRIBUTE_TYPE_REPARSE_POINT EQU 0x000000C0 ; 重解析点(WIN2000/XP)
NTFS_ATTRIBUTE_TYPE_EA_INFORMATION EQU 0x000000D0 ; 附加信息
NTFS_ATTRIBUTE_TYPE_EA EQU 0x000000E0 ; 附加属性
NTFS_ATTRIBUTE_TYPE_PROPERTY_SET EQU 0x000000F0 ; 属性集(WIN2000/XP)
NTFS_ATTRIBUTE_TYPE_LOGGED_UTILITY_STREAM EQU 0x00000100 ; 事务日志
NTFS_ATTRIBUTE_TYPE_FIRST_USER EQU 0x00001000 ; 用户自定义属性起始值
NTFS_ATTRIBUTE_TYPE_END EQU 0xFFFFFFFF ; 表明属性结束
; 属性名称最大长度
NTFS_ATTRIBUTE_NAME_LENGTH EQU 64 ; UNICODE字符长度
; 属性标志
NTFS_ATTRIBUTE_FLAG_COMPRESSED EQU 0x0001 ; 压缩标志
NTFS_ATTRIBUTE_FLAG_ENCRYPTED EQU 0x4000 ; 加密标志
NTFS_ATTRIBUTE_FLAG_SPARSE EQU 0x8000 ; 稀疏文件
NTFS_ATTRIBUTE_RESIDENT_FLAG_INDEXED EQU 0x0001 ; 驻留属性被索引
;============================================================
; 文件属性
;=============================================================
NTFS_FILE_FLAG_READONLY EQU 0x00000001 ; 只读标志
NTFS_FILE_FLAG_HIDDEN EQU 0x00000002 ; 隐藏标志
NTFS_FILE_FLAG_SYSTEM EQU 0x00000004 ; 系统标志
NTFS_FILE_FLAG_VOLUME EQU 0x00000008 ; 卷标标准(NTFS不使用)
NTFS_FILE_FLAG_DIRECTORY EQU 0x00000010 ; 目录属性(NTFS不使用)
NTFS_FILE_FLAG_ARCHIVE EQU 0x00000020 ; 归档标志
NTFS_FILE_FLAG_DEVICE EQU 0x00000040 ; 设备
NTFS_FILE_FLAG_NORMAL EQU 0x00000080 ; 普通属性
NTFS_FILE_FLAG_TEMPORARY EQU 0x00000100 ; 临时文件
NTFS_FILE_FLAG_SPARSE_FILE EQU 0x00000200 ; 稀疏文件
NTFS_FILE_FLAG_REPARSE_POINT EQU 0x00000400 ; 重解析点
NTFS_FILE_FLAG_COMPRESSED EQU 0x00000800 ; 压缩标志
NTFS_FILE_FLAG_OFFLINE EQU 0x00001000 ; 离线
NTFS_FILE_FLAG_NOT_CONTENT_INDEXED EQU 0x00002000 ; 内容没有索引
NTFS_FILE_FLAG_ENCRYPTED EQU 0x00004000 ; 加密文件
NTFS_FILE_FLAG_INDEX_ROOT_PRESENT EQU 0x10000000 ; 拷贝自MFT记录,是否目录(存在IndexRoot)
NTFS_FILE_FLAG_VIEW_INDEX_PRESENT EQU 0x20000000 ; 拷贝自MFT记录,是否存在视图索引(ObjId索引、配额索引等)
;==============================================================================
; 文件名相关常量
;==============================================================================
; 最大允许的文件名长度
NTFS_FILE_NAME_MAXLENGTH EQU 255
; 可能的名字空间
; 最大的命名空间,大小写敏感,除了'/0'和'/'之外的所有Unicode字符都可以作为文件名;
NTFS_FILE_NAME_POSIX EQU 0
; 大小写不敏感,'/0', '"', '*', '/', ':', '<',>', '?', '/' ,'|'都不能用于文件名;
; 并且名字不能以句点(.)和空格结尾;
NTFS_FILE_NAME_WIN32 EQU 1
; 传统的8.3名字,大写字母
NTFS_FILE_NAME_DOS EQU 2
; 兼顾Win32和DOS名字
NTFS_FILE_NAME_WIN32_AND_DOS EQU 3
;==============================================================================
; 卷标志
;==============================================================================
NTFS_VOLUME_FLAG_DIRTY EQU 0x0001 ; 脏标志
NTFS_VOLUME_FLAG_RESIZE_LOG_FILE EQU 0x0002 ; 重设日志文件
NTFS_VOLUME_FLAG_UPGRADE_ON_MOUNT EQU 0x0004 ; 装配时升级
NTFS_VOLUME_FLAG_MOUNTED_ON_NT4 EQU 0x0008 ; 装配标志
NTFS_VOLUME_FLAG_DELETE_USN_UNDERWAY EQU 0x0010 ; 删除USN
NTFS_VOLUME_FLAG_REPAIR_OBJECT_ID EQU 0x0020 ; 修复对象ID
NTFS_VOLUME_FLAG_MODIFIED_BY_CHKDSK EQU 0x8000 ; CHKDSK修改标志
;==============================================================================
; 索引标志
;==============================================================================
; 用于IndexRoot的属性标志
NTFS_INDEX_FLAG_LARGE_INDEX EQU 1 ; 索引存在IndexAllocation属性
; 用于IndexAllocation的属性标志
; 用于IndexEntry的属性标志
NTFS_INDEX_FLAG_INDEX_NODE EQU 1 ; 索引节点(存在子节点)
; 用于IndexEntry的属性标志
NTFS_INDEX_FLAG_INDEX_END EQU 2 ; 指明是最后一个项(结束标志)
;==============================================================================
;
; MFT文件记录引用
;
; 当需要指向MFT中的一个记录时,就需要使用MFT文件记录引用,这是一个64位的数值,
; 由48位的MFT索引号和16位的序列号(用于一致性检查)组成。为了便于报告错误,我
; 们将48位的索引号看成是有符号数;而16位的序列号是一个循环计时器(跳过0),指
; 明被引用的MFT记录被使用的次数;如果该序列号数值位0,则指明不进行序列号一致性
; 检查。
;
;==============================================================================
; NTFS_MFT_REF_MASK 0x0000FFFFFFFFFFFFULL
; NTFS_MAKE_MFT_REF( I,S ) ( (((ULONGLONG)(S)) << 48 ) | (((ULONGLONG)(I)) & NTFS_MFT_REF_MASK) )
; NTFS_MFT_REF_INDEX( R ) ((ULONGLONG)((R) & NTFS_MFT_REF_MASK ))
; NTFS_MFT_REF_SEQUENCE(R) ((USHORT)(((R) >> 48) & 0xFFFF))
; NTFS_IS_MFT_REF_ERR( R ) (((R) & 0x000080000000ULL ) ? 1:0)
;====================================================================
; 结构定义
;====================================================================
;====================================================================
; 带Fixup(TornBits)的记录头,包括FileRecord,IndexAllocation,重启日志等
;====================================================================
STRUC NTFS_RECORD_HEADER
.RecordType RESD 01H ; 记录类型
.UsaOffset RESW 01H ; 更新序列号数组的偏移(相对于记录开始)
.UsaCount RESW 01H ; 更新序列号数组的大小
.RecordLsn RESQ 01H ; 该记录的日志序列号,每次修改时更新
;
; 更新序列号数组( USA: Update Sequence Array )是一个USHORT值数组,该
; 值属于每个由该数组保护的更新序列记录所保护的扇区的末尾信息。
; 注意:该数组的第一个元素是USN( Update Sequence Number ),一个表示
; 记录被写入磁盘的次数的循环计数器。注意值0和-1( 0xFFFF )没有
; 被使用。余下的每个扇区对应的值必须与USN相等(在读取时检查),
; 在写入时设置。
;
ENDSTRUC
;====================================================================
; MFT文件记录
;====================================================================
STRUC NTFS_FILE_RECORD
; 公共记录头
.RecordType RESD 01H ; 记录类型
.UsaOffset RESW 01H ; 更新序列号数组的偏移(相对于记录开始)
.UsaCount RESW 01H ; 更新序列号数组的大小
.RecordLsn RESQ 01H ; 该记录的日志序列号,每次修改时更新
; 文件记录特有属性
.SequenceNumber RESW 01H ; 该记录被使用的序列号(循环计数器,跳过0)
.LinkCount RESW 01H ; 硬连接数,及目录中引用该记录的次数,只在MFT基文件记录中使用;
.AttributesOffset RESW 01H ;第一个属性的偏移,相对于记录开始,必须8字节对齐
.RecordFlags RESW 01H ; 记录属性 NTFS_FILE_RECORD_FLAG_xxx
.BytesInUse RESD 01H ; 记录头和属性的总长度(文件记录的实际长度),8字节对齐
.BytesAllocated RESD 01H ; 总共分配给文件记录的长度(应该与BytesPerFileRecord一致)
.BaseFileRecord RESQ 01H ; 基本文件记录中的文件索引号(对于基本文件记录,其值为0)
.NextAttributeNumber RESW 01H ; 下一个属性ID,注意第一个属性ID为0;每次增1并在重用是复位为0;
; 以下两项出现在NTFS 3.1+ ( Windows XP及以上版本 _
.Reserved RESW 01H ; 保留字节用于对齐
.FileRecordNumber RESD 01H ; 本记录的索引号
;
; 当使用MFT记录时,将USA(更新序列号数组)放在这个位置,即在第一个属性开始
; 之前的位置。
;
ENDSTRUC
;====================================================================
; 属性定义结构
;====================================================================
STRUC NTFS_ATTRIBUTE_DEFINITION
.AttributeName RESB NTFS_ATTRIBUTE_NAME_LENGTH ;属性名称
.AttribyteType RESD 01H ; 属性类型
.DisplayRule RESD 01H ; 默认显示规则
.CollationRule RESD 01H ; 默认排序规则
.AttributeFlags RESD 01H ; 属性标志
.MinimumSize RESQ 01H ; 属性最小长度
.MaximumSize RESQ 01H ; 属性最大长度
ENDSTRUC
;====================================================================
; 公共属性头
;====================================================================
STRUC NTFS_ATTRIBUTE
.AttributeType RESD 01H ; 属性类型
.Length RESD 01H ; 驻留部分的长度
.Nonresident RESB 01H ; 指明是否非驻留属性
.NameLength RESB 01H ; 属性名称长度(UNICODE)
.NameOffset RESW 01H ; 名称偏移
.AttributeFlags RESW 01H ; 属性标志
.AttributeNumber RESW 01H ; 属性编号(在文件记录内唯一)
ENDSTRUC
;====================================================================
; 驻留属性
;====================================================================
STRUC NTFS_RESIDENT_ATTRIBUTE
; 公共属性头
.AttributeType RESD 01H ; 属性类型
.Length RESD 01H ; 驻留部分的长度
.Nonresident RESB 01H ; 指明是否非驻留属性
.NameLength RESB 01H ; 属性名称长度(UNICODE)
.NameOffset RESW 01H ; 名称偏移
.AttributeFlags RESW 01H ; 属性标志
.AttributeNumber RESW 01H ; 属性编号(在文件记录内唯一)
; 驻留属性
.ValueLength RESD 01H ; 属性值的长度
.ValueOffset RESW 01H ; 属性偏移(如果存在名字,则需要8字节对齐)
.ResidentFlags RESW 01H ; 驻留属性标志
ENDSTRUC
;====================================================================
; 非驻留属性
;====================================================================
STRUC NTFS_NONRESIDENT_ATTRIBUTE
; 公共属性头
.AttributeType RESD 01H ; 属性类型
.Length RESD 01H ; 驻留部分的长度
.Nonresident RESB 01H ; 指明是否非驻留属性
.NameLength RESB 01H ; 属性名称长度(UNICODE)
.NameOffset RESW 01H ; 名称偏移
.AttributeFlags RESW 01H ; 属性标志
.AttributeNumber RESW 01H ; 属性编号(在文件记录内唯一)
; 非驻留属性
.LowVcn RESQ 01H ; 该属性片断的起始VCN(虚拟簇号),只有存在AttributeList时该值才不为0;
.HighVcn RESQ 01H ; 该属性片断的终止VCN (-1表示长度为0)
.RunArrayOffset RESW 01H ; DataRun数组相对于属性开始处的偏移(8字节对齐)
.CompressionUnit RESB 01H ; 压缩单元,代表簇数的2的幂;0代表不压缩;WINNT只使用值4,代表压缩单位为16簇。
.Reserved RESB 05H ; 保留字节用于对齐
.AllocatedSize RESQ 01H ; 分配的磁盘空间;当使用压缩时,它为压缩块的倍数,并表示逻辑大小。
.DataSize RESQ 01H ; 数据的真实大小;
.InitializedSize RESQ 01H ; 初始化大小,一般等于DataSize
.CompressedSize RESQ 01H ; 压缩后的大小(真实磁盘空间大小)
ENDSTRUC
;====================================================================
; 标准属性(驻留)
;====================================================================
STRUC NTFS_STANDARD_INFORMATION
.CreationTime RESQ 01H ; 创建时间,当修改文件名时更新;
.ChangeTime RESQ 01H ; 修改时间,当数据属性被修改时更新。
.LastWriteTime RESQ 01H ; 最后写入时间,当MFT文件记录被修改时更新。
.LastAccessTime RESQ 01H ; 最后访问时间,对只读介质,不更新;可以取消该字段的更新(性能考虑);
.FileAttributes RESD 01H ; 文件属性
.MaximumVersion RESD 01H ; 最大允许的文件版本号,如果不启用版本则为0;
.CurrentVersion RESD 01H ; 当前文件版本号,不使用版本时为0;
.ClassId RESD 01H ; 类标识符索引(?)
.QuotaId RESD 01H ; 文件所有者ID,用于配额控制
.SecurityId RESD 01H ; 文件安全ID
.QuotaCharge RESQ 01H ; 配额大小(如果不使用配额,则为0);
.Usn RESQ 01H ; 最后一次更新日志序列号。
ENDSTRUC
;==============================================================================
;
; 属性列表
;
;==============================================================================
;
; ·属性列表可以是驻留的(如果足够小),也可以是非驻留的;
; ·由一系列变长的、8字节对齐的NTFS_ATTRIBUTE_LIST_ENTRY项所组成;
; ·属性列表中对文件的每个属性都有一条相应的记录项(除了AttributeList自身),
; 属性列表是排序的:首先按属性类型排序,然后按属性名称排序,最后根据
; 属性编号排序;扩展的非驻留属性紧跟在初始的区域之后,并且按照LowVCN排序;
; 同时属性编号设为0;
; ·如果是非驻留的,那么VCN到LCN的映射数组必须能够容纳在基文件记录中;
; ·属性列表的最大尺寸为256K,这是由Windows缓存管理器决定的;
; ·只有当MFT记录在将必须驻留的属性排除之后,所有可非驻留属性不能容纳在文件
; 记录当中时才使用属性列表;例如:文件有大量的硬连接;因为磁盘碎片,VCN到
; LCN的映射数组变得过大;有很多命名数据流;
;
STRUC NTFS_ATTRIBUTE_LIST_ENTRY
.AttributeType RESD 01H ; 所存储的属性类型
.Length RESW 01H ; 该项的长度
.NameLength RESB 01H ; 名字长度(UNICODE)
.NameOffset RESB 01H ; 名字偏移(即使不使用名字时该值总是正确设置)
.LowVcn RESQ 01H ; 该属性值片断的最小虚拟簇号
.FileRecordNumber RESQ 01H ; 主文件记录索引
.AttributeNumber RESW 01H ; 如果LowVcn =0,则是属性的编号;否则,为0;
.AttributeName RESW 01H ; 属性名称,如果存在的话
ENDSTRUC
;====================================================================
; 文件名称属性
;====================================================================
STRUC NTFS_FILE_NAME
.ParentDirectory RESQ 01H ; 父目录的MFT记录索引
.CreationTime RESQ 01H ; 创建时间,当名字被修改时更新;
.ChangeTime RESQ 01H ; 名字被最后修改的时间
.LastWriteTime RESQ 01H ; MFT记录被最后修改的时间
.LastAccessTime RESQ 01H ; MFT记录最后被访问的时间
.AllocatedSize RESQ 01H ; 分配大小
.DataSize RESQ 01H ; 实际大小
.FileAttributes RESD 01H ; 文件属性
.ReparsePointTag RESD 01H ; 重解析点标志
.NameLength RESB 01H ; 名字长度(UNICODE)
.NameType RESB 01H ; 名字空间类型
.Name RESW 01H ; 文件名
ENDSTRUC
;====================================================================
; 索引项头部
;====================================================================
STRUC NTFS_INDEX_HEADER
.EntriesOffset RESD 01H ; 索引入口的偏移(8字节对齐)
.IndexBlockLength RESD 01H ; 索引项目的大小(8字节对齐)
.AllocatedSize RESD 01H ; 索引分配的大小(8字节对齐)
.IndexFlags RESD 01H ; 索引标志
ENDSTRUC
;====================================================================
;
; 索引根属性(IndexRoot)中是驻留属性,由一系列NTFS_INDEX_ENTRY结构紧随其后。
; 当目录很小,在IndexRoot中能够容纳所有的索引节点时,只有IndexRoot属性存在;
; 当目录很大,在IndexRoot中容纳不下的时候,两个额外的属性IndexAllocation和
; Bitmap属性存在(描述哪个虚拟簇号在IndexAllocation中被使用)。
; 注意:文件系统根目录(.)包含自身的一个项;其他目录不包含自身;
;
;====================================================================
STRUC NTFS_INDEX_ROOT
.AttributeType RESD 01H ; 索引属性类型(对于目录是$FILE_NAME,对于视图索引为0。
.CollationRule RESD 01H ; 排序规则,当类型为文件名时,必须为COLLATION_FILE_NAME.
.BytesPerIndexBlock RESD 01H ; 索引块的大小
.ClustersPerIndexBlock RESB 01H ; 索引块的簇数;如果小于0,则为2的幂;
.Reserved RESB 03H ; 对齐字节
; IndexHeader
.EntriesOffset RESD 01H ; 索引入口的偏移(8字节对齐)
.IndexBlockLength RESD 01H ; 索引项目的大小(8字节对齐)
.AllocatedSize RESD 01H ; 索引分配的大小(8字节对齐)
.IndexFlags RESD 01H ; 索引标志
ENDSTRUC
;====================================================================
; 索引数据块 IndexAllocation(总是非驻留的)
;====================================================================
STRUC NTFS_INDEX_BLOCK
; 公共记录头
.RecordType RESD 01H ; 记录类型
.UsaOffset RESW 01H ; 更新序列号数组的偏移(相对于记录开始)
.UsaCount RESW 01H ; 更新序列号数组的大小
.RecordLsn RESQ 01H ; 该记录的日志序列号,每次修改时更新
; 索引块字段
.IndexBlockVcn RESQ 01H ; 索引块的虚拟簇号
; IndexHeader
.EntriesOffset RESD 01H ; 索引入口的偏移(8字节对齐)
.IndexBlockLength RESD 01H ; 索引项目的大小(8字节对齐)
.AllocatedSize RESD 01H ; 索引分配的大小(8字节对齐)
.IndexFlags RESD 01H ; 索引标志
; 注意:Usa置于此处;
; 接下来就是具体的索引项
; 如果有子节点,则最后的8字节数据为子节点VCN
ENDSTRUC
;=============================================================
; 索引项头(忽略了非文件名索引,如$R)
;=============================================================
STRUC NTFS_INDEX_ENTRY
.IndexedFile RESQ 01H ; 目录索引所引用的文件在MFT中的记录号
.Length RESW 01H ; 该索引项的长度(8字节对齐)
.KeyLength RESW 01H ; 键值长度(注意:非8字节对齐)
.EntryFlags RESW 01H ; 索引项标志,指明是否有子节点,以及是否结束项
.Alignment RESW 01H ; 对齐字节
; 接下来就是索引键及附加数据
; 这里只列出了NTFS_FILE_NAME
.ParentDirectory RESQ 01H ; 父目录的MFT记录索引
.CreationTime RESQ 01H ; 创建时间,当名字被修改时更新;
.ChangeTime RESQ 01H ; 名字被最后修改的时间
.LastWriteTime RESQ 01H ; MFT记录被最后修改的时间
.LastAccessTime RESQ 01H ; MFT记录最后被访问的时间
.AllocatedSize RESQ 01H ; 分配大小
.DataSize RESQ 01H ; 实际大小
.FileAttributes RESD 01H ; 文件属性
.ReparsePointTag RESD 01H ; 重解析点标志
.NameLength RESB 01H ; 名字长度(UNICODE)
.NameType RESB 01H ; 名字空间类型
.Name RESW 01H ; 文件名
ENDSTRUC
;====================================================================
;
; 启动扇区(最多16个扇区)
;
;====================================================================
_ENTRY_POINT:
; 3字节的跳转指令
JMP SHORT _BOOT_CODE ; 跳转到真正的引导代码
NOP ; 空指令以保证字节数为3
; 8字节的OEMName
OEMName DB "NTFS "
;====================================================================
;
; BPB( BIOS Parameter Block )
;
;====================================================================
BytesPerSector DW ? ; 每个扇区的字节数 (512 1024 2048 4096)
SectorsPerCluster DB ? ; 每个簇的扇区数 ( 1 2 4 8 16 32 64 128 )
; 两者相乘不能超过32K(簇最大大小)
ReservedSectors DW ? ; 从卷的第一个扇区开始的保留扇区数目;
; 该值不能为0,对于FAT12/FAT16,该值通常为1;
; 对于FAT32,典型值为32;
NumberOfFATs DB ? ; 卷上FAT数据结构的数目,该值通常应为2
; [NTFS不使用NumberOfFATs字段,必须为0]
RootEntries DW ? ; 对于FAT12/FAT16,该值表示32字节目录项的数目;
; 对于FAT32,该值必须为0;[NTFS不使用]
NumberOfSectors16 DW ? ; 该卷上的扇区总数,该字段可以为0,如果该字段
; 为0,则NumberOfSectors32不能为0;对于FAT32,
; 该字段必须为0 [FAT32/NTFS不使用该字段]
MediaDescriptor DB ? ; 介质类型
SectorsPerFAT16 DW ? ; 该字段标识一个FAT结构占有的扇区数(FAT12/FAT16),
; 对于FAT32卷,该字段必须为0;[FAT32/NTFS不使用该字段]
SectorsPerTrack DW ? ; 用于INT 0x13中断的每个磁道的扇区数
HeadsPerCylinder DW ? ; 用于INT 0x13中断的每个柱面的磁头数
HiddenSectors DD ? ; 包含该FAT卷的分区之前的隐藏扇区数
NumberOfSectors32 DD ? ; 该字段包含该卷上的所有扇区数目,对于FAT32,该字段
; 不为0;FAT12/FAT16可根据实际大小是否超过65536个扇
; 区数决定是否采用该字段; [NTFS不使用该字段]
;====================================================================
;
; EBPB ( Extended BIOS Parameter Block )
;
;====================================================================
UnknownFlags DD ? ; 未知用途标志字段,总是为0x00800080或类似的值
NumberOfSectors64Low DD ? ; 扇区总数低32位
NumberOfSectors64High DD ? ; 扇区总数高32位
MftStartLcnLow DD ? ; 主文件表(MFT: Master File Table)的逻辑簇号
MftStartLcnHigh DD ? ; Logical Cluster Number for the file $MFT
Mft2StartLcnLow DD ? ; 主文件表镜像(备份)的逻辑簇号
Mft2StartLcnHigh DD ? ; Logical Cluster Number for the file $MFTMirr
ClustersPerFRS DB ? ; 每文件记录段的簇数(必须为2的幂,负数表示位移量)
Reserved0 TIMES 3 DB ? ; 保留对齐字节
ClustersPerIAB DB ? ; 缺省的每索引分配缓冲的簇数(必须为2的幂,负数表示位移量)
Reserved1 TIMES 3 DB ? ; Default Clusters Per Index Allocation Buffer
SerialNumberLow DD ? ; 卷序列号( Volume Serial Number )
SerialNumberHigh DD ? ; 卷序列号( Volume Serial Number )
CheckSum DD ? ; 校验和
;====================================================================
;
; 真正的启动代码从这开始
; 其功能是搜索磁盘的根目录,查找FDOSLDR.BIN文件,将其读入内存并运行。
;
;====================================================================
_BOOT_CODE:
; 初始化相关寄存器及标志位
CLI ; 先关掉中断
CLD ; 方向为向前递增
XOR AX,AX ; AX = 0
MOV DS,AX ; 设置数据段寄存器 DS:SI
MOV ES,AX ; 设置附加段寄存器 ES:DI
MOV SS,AX ; 设置堆栈段寄存器
MOV BP,8000H ; 设置基址寄存器
MOV SP,STACK_ADDR ; 设置堆栈栈顶
STI ; 允许中断
;====================================================================
; 保存启动的磁盘编号
;====================================================================
MOV [BP-DRIVE_NUMBER],DL; 该值由BIOS设置,如果是从USB启动,该值为0x80
; 即为第一个硬盘的编号,该值将用于后续的磁盘
; 读取调用
;====================================================================
; 检查是否支持磁盘中断INT 13H的扩展
;====================================================================
MOV BYTE [BP - DISK_EXT_SUPPORT],00H ; 00H表示不支持磁盘扩展
MOV AH,41H
MOV BX,055AAH
INT 13H
JC .LoadSectors ; 如果失败,进位标志为1或者BX值不对( AA55 or 55AA )
; 设置磁盘支持扩展中断标志
MOV BYTE [BP - DISK_EXT_SUPPORT],01H ; 01H表示支持磁盘扩展
; 不支持磁盘扩展
.LoadSectors:
;====================================================================
;
; 初始化DiskAddressPacket
; 使用时只需要修改字段:DATA_BUFFER_OFF DATA_BUFFER_SEG
; DAP_SECTOR_LOW DAP_SECTOR_HIGH
;
;====================================================================
MOV DWORD [BP - DAP_SECTOR_HIGH ],00H ; 起始扇区号
MOV DWORD [BP - DAP_SECTOR_LOW ],00H
MOV WORD [BP - DAP_BUFFER_SEG ],00H ; 缓冲区段地址
MOV BYTE [BP - DAP_RESERVED1 ],00H
MOV BYTE [BP - DAP_RESERVED2 ],00H
MOV BYTE [BP - DAP_PACKET_SIZE ],10H
MOV WORD [BP - DAP_BUFFER_OFF ],BOOT_SEC_ADDR ; 缓冲区偏移
MOV BYTE [BP - DAP_READ_SECTORS],BOOT_SEC_NUM ; 最多16个扇区
; 装载第二个启动扇区
MOV EAX , DWORD[HiddenSectors]
MOV DWORD [BP - DAP_SECTOR_LOW ],EAX
CALL ReadSectors
JMP 0:.RealStart
; 真正开始执行代码的地方
.RealStart:
; 计算常用参数
; 每簇字节数
XOR EAX,EAX
MOV AX, WORD[BytesPerSector]
XOR ECX,ECX
MOV CL, BYTE[SectorsPerCluster]
MUL ECX
MOV DWORD[BP - BYTES_PER_CLUSTER],EAX
;%IFDEF DEBUG
; CALL PrintDword
; MOV AL,20H
; CALL PrintChar
;%ENDIF
; 每文件记录的字节数
XOR EAX,EAX
MOV AL,BYTE[ClustersPerFRS]
CALL GetRealSizeFromClusters ; EAX = 字节数
MOV DWORD[BP - BYTES_PER_FILE_RECORD],EAX
;%IFDEF DEBUG
; CALL PrintDword
; MOV AL,20H
; CALL PrintChar
;%ENDIF
; 计算每索引分配块大小
XOR EAX,EAX
MOV AL,BYTE[ClustersPerIAB]
CALL GetRealSizeFromClusters ; EAX = 字节数
MOV DWORD[BP - BYTES_PER_INDEX_BLOCK],EAX
;%IFDEF DEBUG
; CALL PrintDword
; MOV AL,20H
; CALL PrintChar
;%ENDIF
; 下面开始查找根目录并且装载FDOSLDR.BIN
JMP _SEARCH_LOADER
;====================================================================
; 错误处理
;====================================================================
_MISSING_LOADER: ; 显示没有装载程序
MOV SI,MessageMissLoader
CALL ShowMessage
JMP _REBOOT
_DISK_ERROR: ; 显示磁盘错误信息
MOV SI,MessageDiskError
CALL ShowMessage
_REBOOT: ; 重启
MOV SI,MessageRestart
CALL ShowMessage
; 调用键盘中断,等待用户按键
MOV AH,00H
INT 16H
; 重启计算机
INT 19H
; 死循环
JMP $
;====================================================================
;
; 子过程
;
;====================================================================
;====================================================================
;
; 计算文件记录或索引分配块的字节数
; 输入:AL = 用字节表示的簇的倍数
; 输出:EAX = 真实的字节数
;
;====================================================================
GetRealSizeFromClusters:
PUSH ECX
PUSH EDX
; 首先保存相对簇数
XOR ECX,ECX
MOV CL,AL
CMP CL,00H
JLE .ShiftBits ; 有符号比较
; ClusersNumber > 0
MOV EAX,DWORD[BP - BYTES_PER_CLUSTER]
MUL ECX ; EDX:EAX = 字节数
JMP .CalculateOK
; 负数,为相对于1的唯一
.ShiftBits:
NEG CL
MOV EAX,1
SHL EAX,CL
.CalculateOK:
POP EDX
POP ECX
RET
;====================================================================
;
; 读取一个或多个磁盘扇区
; 输入: 已经设置了DAP中相应的字段
;
;====================================================================
ReadSectors:
PUSHA ; 保存寄存器
; 检查是否使用扩展方式
CMP BYTE [BP - DISK_EXT_SUPPORT],00H
JZ .NoDiskExtension
;====================================================================
; INT 13H AH = 42H 扩展磁盘调用
;====================================================================
MOV AH,42H ; 功能号
LEA SI ,[BP - DAP_PACKET_SIZE] ; 地址包地址
MOV DL ,[BP - DRIVE_NUMBER] ; 驱动器号
INT 13H
JC _DISK_ERROR ; 读取失败
JMP _READ_SECTOR_OK ; 读取成功
;====================================================================
;
; INT 13H
; AH = 2 柱面号:0 - 1023
; AL = 要读取的扇区数 磁头号:0 - 255
; CH = 柱面号低8位 扇区号:1 - 63
; CL = 柱面号高2位 : 6位扇区号
; DH = 磁头号
; DL = 驱动器号 ES:BX = 缓冲区地址
;
; LBA = ( (cylinder * HeadsPerCylinder + heads ) * SectorsPerTrack ) + sector - 1
;
; Sector = LBA % SectorsPerTrack +1
; Head = ( LBA / SectorsPerTrack ) % HeadsPerCylinder
; Cylinder= ( LBA / SectorsPerTrack ) / HeadsPerCylinder
;
;====================================================================
.NoDiskExtension:
;====================================================================
; 首先需要将扇区号转换为CHS地址
;====================================================================
; 首先计算扇区号
MOV AX,WORD [ BP - DAP_SECTOR_LOW ]
MOV DX,WORD [ BP - DAP_SECTOR_LOW+2 ]
DIV WORD [SectorsPerTrack ] ; AX = LBA / SectorsPerTrack DX = LDA % SectorsPerTrack
MOV CX,DX
INC CX ; CL = Sector
AND CL,3FH ; 1-63
; 再计算磁头号和柱面号
XOR DX,DX ; DX:AX = LBA / SectorsPerTrack
DIV WORD [HeadsPerCylinder] ; DX = ( LBA/SectorsPerTrack ) % HeadsPerCylinder = Head
; AX = ( LBA/SectorsPerTrack ) / HeadsPerCylinder = Cylinder
MOV CH,AL ; 柱面号低8位
SHL AH,6
OR CL,AH ; CL = 柱面号高2位:6位扇区号
MOV DH,DL ; DL = 磁头号
; 准备读取磁盘
MOV AX,WORD[ BP - DAP_BUFFER_SEG ] ; ES:BX = 缓冲区地址
MOV ES,AX
MOV BX,WORD[ BP - DAP_BUFFER_OFF ]
MOV AH,02H ; 功能号
MOV AL,BYTE[ BP - DAP_READ_SECTORS] ; 要读取的扇区数
MOV DL ,[BP - DRIVE_NUMBER] ; 驱动器号
INT 13H
JC _DISK_ERROR ; 读取失败
_READ_SECTOR_OK:
POPA ; 恢复寄存器
RET
;====================================================================
;
; 显示一个字符串
; 输入:
; DS:SI = 字符串的起始地址(以NULL结束)
;
;====================================================================
ShowMessage:
LODSB ; AL = DS:[SI] SI = SI+1
OR AL,AL ; 检测是否遇到NULL字符串
JZ _SHOW_END
MOV AH,0EH
MOV BX,07H
INT 10H
JMP ShowMessage
_SHOW_END:
RET
;====================================================================
; 数据区
;====================================================================
; 第二阶段启动程序 FDOSLDR.BIN
LoaderName DB 46H,00H ; F
DB 44H,00H ; D
DB 4FH,00H ; O
DB 53H,00H ; S
DB 4CH,00H ; L
DB 44H,00H ; D
DB 52H,00H ; R
DB 2EH,00H ; .
DB 42H,00H ; B
DB 49H,00H ; I
DB 4EH,00H ; N
MessageMissLoader DB "NO FDOSLDR.BIN.",0DH,0AH,00H ; 没有找到装载程序
MessageDiskError DB "Disk Error.",0DH,0AH,00 ; 磁盘错误消息
MessageRestart DB "Press any key to restart." ,0DH,0AH,00 ; 提示重启消息
;====================================================================
; 扇区最后的标记字节(NASM不支持重复ORG)
;====================================================================
Padding TIMES 510-($-$$) DB 00H
SectorSignature DW 0AA55H
;====================================================================
; 第二个扇区的代码(该代码位于分区的第二个扇区)
;====================================================================
; 下面开始查找根目录并且装载FDOSLDR.BIN
_SEARCH_LOADER:
;
; 首先装载MFT,然后获得根目录的文件记录
; 根据MftStartLcn读取一定的数据,必须确保根目录的文件记录也被读进来了
;
; 计算需要读取的扇区数
MOV EAX,NTFS_SYSTEM_FILE_ROOT
INC EAX
MOV ECX,DWORD[BP - BYTES_PER_FILE_RECORD]
MUL ECX ; EDX:EAX = 6*BytePerFileRecord
XOR ECX,ECX
MOV CX,WORD[BytesPerSector]
ADD AX,CX
DEC EAX
DIV ECX ; EAX = 需要读取的簇数
MOV EBX,EAX ; EBX = 需要读取的簇数
; 读取MFT记录
MOV EAX,DWORD[MftStartLcnLow]
MOV EDX,DWORD[MftStartLcnHigh]
XOR ECX,ECX
MOV CL, BYTE[SectorsPerCluster]
MUL ECX ; EDX:EAX = 起始扇区号
; 构造读取数据包
MOV DWORD[ BP - DAP_SECTOR_HIGH ], EDX
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV WORD [ BP - DAP_BUFFER_SEG ], DATA_BUF_SEG
MOV WORD [ BP - DAP_BUFFER_OFF ], DATA_BUF_OFF
MOV BYTE [ BP - DAP_READ_SECTORS], BL
CALL ReadSectors
; 将根目录记录拷贝到80000处
MOV AX,FILE_BUF_SEG
MOV ES,AX
MOV EDI,FILE_BUF_OFF
; 计算记录偏移
MOV EAX,NTFS_SYSTEM_FILE_ROOT
MOV ECX,DWORD[BP - BYTES_PER_FILE_RECORD]
MUL ECX ; EDX:EAX = 6*BytePerFileRecord
ADD EAX,DATA_BUF_OFF
MOV ESI,EAX
MOV AX,DATA_BUF_SEG
MOV DS,AX
REP MOVSB
;恢复目标地址
MOV EDI,FILE_BUF_OFF
; 恢复数据段寄存器
XOR AX,AX
MOV DS,AX
; 调整每扇区末尾字节
; ES:EDI = FileRecord
CALL FixupRecord
; 现在开始查找IndexRoot和IndexAllocation属性
MOV EAX,NTFS_ATTRIBUTE_TYPE_INDEX_ROOT
CALL FindAttribute ; EAX = Attribute相对于记录开始位置的偏移
CMP EAX,0
JZ _MISSING_LOADER
; 首先在IndexRoot中查找是否有FDOSLDR.BIN文件的存在
; ES:EDI = FileRecord EAX = IndexRoot属性的偏移
CALL SearchIndexRoot
;检查是否找到了
; EDX:EAX = FileIndex
AND EDX,0000FFFFH
CMP EDX,0
JNZ .LoadLoader
CMP EAX,0
JNZ .LoadLoader
; 没有找到 EDX == 0 && EAX == 0 => NOT FOUND
JMP _MISSING_LOADER
;====================================================================
; 找到了FDOSLDR.BIN文件的FileIndex
;====================================================================
.LoadLoader:
;====================================================================
; 首先读取该文件的文件记录
;====================================================================
; 我们现在已经得到了FDOSLDR.BIN文件的文件索引号
; 我们假设MFT的连续性没有问题,即不用去查DataRunList,直接根据MFTStartLcn来计算偏移
; EDX:EAX = FileIndex
;
MOV ECX,DWORD[EBP-BYTES_PER_FILE_RECORD]
MOV EBX,DWORD[EBP-BYTES_PER_CLUSTER]
MUL ECX ; EDX:EAX = FileIndex*BytesPerFileRecord
DIV EBX ; EDX = BytesOffsetInCluster EAX = VCN
MOV EBX,EDX ; EBX = BytesOffsetInCluster EAX = VCN
XOR EDX,EDX
; 假设FDOSLDR.BIN位于MFT的第一个RUN上
; 注意:这个假设可能不成立
ADD EAX,DWORD[MftStartLcnLow]
ADC EDX,DWORD[MftStartLcnHigh]
; EDX : EAX = StartLcn
XOR ECX,ECX
MOV CL,BYTE[SectorsPerCluster]
MUL ECX ; EDX:EAX = 起始扇区号
; 构造读取数据包
MOV DWORD[ BP - DAP_SECTOR_HIGH ], EDX
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV WORD [ BP - DAP_BUFFER_SEG ], DATA_BUF_SEG
MOV WORD [ BP - DAP_BUFFER_OFF ], DATA_BUF_OFF
; 计算需要读取的扇区数
XOR ECX,ECX
XOR EDX,EDX
MOV EAX,DWORD[EBP-BYTES_PER_FILE_RECORD]
MOV CX,WORD[BytesPerSector]
DIV ECX
MOV BYTE [ BP - DAP_READ_SECTORS], AL
CALL ReadSectors
; 将文件记录拷贝到80000处
MOV AX,FILE_BUF_SEG
MOV ES,AX
MOV EDI,FILE_BUF_OFF
MOV EAX,DATA_BUF_OFF
MOV ESI,EAX
MOV AX,DATA_BUF_SEG
MOV DS,AX
REP MOVSB
;恢复目标地址
MOV EDI,FILE_BUF_OFF
; 恢复数据段寄存器
XOR AX,AX
MOV DS,AX
; 调整每扇区末尾字节
; ES:EDI = FileRecord
CALL FixupRecord
;====================================================================
; 然后查找该文件的$DATA数据流
;====================================================================
; ES:EDI = FileRecord
; 现在开始查找Data属性
MOV EAX,NTFS_ATTRIBUTE_TYPE_DATA
CALL FindAttribute ; EAX = Attribute相对于记录开始位置的偏移
CMP EAX,0
JZ _MISSING_LOADER
; EAX = Attribute相对于记录开始位置的偏移
; 因为FDOSLDR.BIN肯定大于1KB,所以其数据属性必定是非驻留的
; 我们假定FDOSLDR.BIN不会形成文件碎片,只有一个RUN
; ESI = 文件的尺寸
MOV ESI,DWORD[ES:EDI+EAX+NTFS_NONRESIDENT_ATTRIBUTE.DataSize]
XOR ECX,ECX
MOV CX,WORD[ES:EDI+EAX+NTFS_NONRESIDENT_ATTRIBUTE.RunArrayOffset]
ADD EAX,ECX ; EAX = DataRun
; 对偏移进行解码,获得目标LCN
; 输入: ES:EDI = FileRecord EBX = DataRun EDX:EAX = VCN
; 输出: EDX:EAX= LCN 0代表错误
MOV EBX,EAX ; EBX = DataRun
XOR EAX,EAX
XOR EDX,EDX ; StartVcn = 0
CALL DecodeDataRunList
; 假设32位(不管高32位)
CMP EAX,00H
JZ _DISK_ERROR
;====================================================================
; 现在得到了文件的起始逻辑簇号 EDX:EAX = StartLcn,可以开始读取文件了
;====================================================================
; 假设只有一个DATARUN(即不存在碎片)
;找到了Lcn 读取该文件
; 构造读取数据包
XOR ECX,ECX
MOV CL, BYTE[SectorsPerCluster]
MUL ECX ; EDX:EAX = 起始扇区号
MOV DWORD[ BP - DAP_SECTOR_HIGH ], EDX
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
; 计算需要读取的扇区数
XOR EDX,EDX
MOV EAX,ESI ; ESI = 文件尺寸
XOR ECX,ECX
MOV CX,WORD[BytesPerSector]
ADD EAX,ECX
DEC EAX ; EAX = 文件尺寸+BytesPerSector-1
DIV ECX ; EAX = 需要读取的扇区数
MOV ECX,EAX ; ECX = 需要读取的总扇区数
; 起始缓冲区地址
MOV WORD [ BP - DAP_BUFFER_SEG ], OSLOADER_SEG
MOV WORD [ BP - DAP_BUFFER_OFF ], 0
MOV BYTE [ BP - DAP_READ_SECTORS], 1 ; 每次读取一个扇区
.ReadNextSector:
; 读取该扇区
CALL ReadSectors
; 更新扇区号
INC DWORD[ BP - DAP_SECTOR_LOW ]
; 更新缓冲区地址
XOR EAX,EAX
MOV AX,WORD[BytesPerSector]
SHR AX,4 ; AX = BytesPerSector/16
ADD WORD [ BP - DAP_BUFFER_SEG ],AX
;检查是否还有扇区需要读取
DEC ECX
JNZ .ReadNextSector
; 读取完毕,可以跳转到FDOSLDR.BIN执行
_RUN_LOADER:
%IFDEF DEBG
MOV ESI,OSLOADER_ADDR
MOV EAX,DWORD[DS:ESI]
CALL PrintDword
MOV AL,20H
CALL PrintChar
%ENDIF
; 运行FDOSLDR.BIN
MOV DL,BYTE[EBP-DRIVE_NUMBER] ; BYTE 用于保存启动的磁盘驱动器号
JMP 00:OSLOADER_ADDR
;====================================================================
; 调整每扇区末尾字节
;
; 输入:ES:EDI = FileRecord
; 输出:调整了相应字节
;====================================================================
FixupRecord:
PUSHA
XOR EAX,EAX
XOR EBX,EBX
XOR ECX,ECX
XOR EDX,EDX
; 首先得到USN和UsaCount
MOV AX,WORD [ES:EDI+NTFS_RECORD_HEADER.UsaOffset] ; AX = USN
MOV CX,WORD [ES:EDI+NTFS_RECORD_HEADER.UsaCount ]
DEC CX ; CX = UsaCount - 1
; 更新数据块
MOV BX,WORD[BytesPerSector]
DEC BX
DEC BX ; BX = BytesPerSecor - 2
CMP CX,0
JLE .FixupEnd
; USA
MOV DX,NTFS_RECORD_HEADER.UsaOffset
INC DX,
INC DX ; SKIP USN
; 更新每个扇区
.LoopAgain:
MOV AX, WORD [ES:EDI+EDX]
MOV WORD [ES:EDI+EBX],AX
INC DX,
INC DX
ADD BX,WORD[BytesPerSector]
LOOP .LoopAgain
.FixupEnd:
POPA
RET
;====================================================================
; 查找某个属性
;
; 输入:ES:EDI = FileRecord EAX = 属性类型
; 输出:EAX = 属性的偏移位置,如果为0则表示没有找到
; 限制:目前没有实现AttributeList,假定根目录不会有很多文件或子目录。
;====================================================================
FindAttribute:
PUSH EBX
PUSH ECX
PUSH EDX
XOR EBX,EBX
XOR ECX,ECX
XOR EDX,EDX
MOV BX, WORD[ ES:EDI+EBX+NTFS_FILE_RECORD.AttributesOffset]
; 检查下一个属性
.CheckNextAttribute:
; 检查是否为最后一个属性
MOV ECX,DWORD[ ES:EDI+EBX+NTFS_ATTRIBUTE.AttributeType]
CMP ECX,NTFS_ATTRIBUTE_TYPE_END
JZ .NotFound ; 已经没有可找的了
; 检查是否我们需要的类型
CMP ECX,EAX
JZ .Found ; 找到了
; 检查下一个属性
ADD EBX,DWORD[ ES:EDI+EBX+NTFS_ATTRIBUTE.Length]
; 检查是否超过范围
CMP EBX,DWORD[BP - BYTES_PER_FILE_RECORD]
JB .CheckNextAttribute
.NotFound:
MOV EBX,0
.Found:
MOV EAX,EBX
; 恢复寄存器
POP EDX
POP ECX
POP EBX
RET
;====================================================================
; 在IndexRoot中查找FDOSLDR.BIN或其应该位于的子节点
;
; 输入:ES:EDI = FileRecord EAX = IndexRoot属性的偏移
; 输出:
;
;====================================================================
SearchIndexRoot:
PUSH EBX
PUSH ECX
SUB ESP,16 ; 保存子节点的VCN
; 00H EAX
; 04H EDX
; 08H BytesPerIndexBlock
; IndexRoot总是驻留属性
MOV EBX,EAX
XOR ECX,ECX
MOV CX, WORD[ES:EDI+EBX+NTFS_RESIDENT_ATTRIBUTE.ValueOffset]
ADD EBX,ECX ; EBX = INDEX_ROOT
MOV EDX,DWORD[ES:EDI+EBX+NTFS_INDEX_ROOT.BytesPerIndexBlock]
MOV DWORD[ESP+08H],EDX; BytesPerIndexBlock
MOV ECX,DWORD[ES:EDI+EBX+NTFS_INDEX_ROOT.IndexFlags]
AND ECX,NTFS_INDEX_FLAG_LARGE_INDEX ; 是否有IndexAllocation
JZ NEAR .SmallIndexRoot
;====================================================================
; 存在IndexAllocation的目录(大目录)
;====================================================================
.LargeIndexRoot:
; EDX = BytesPerIndexBlock
; EBX = INDEX_ROOT
; ES:EDI = FileRecord
;====================================================================
; 首先收缩IndexRoot,得到下一级的子节点
;====================================================================
; EBX = IndexRoot
MOV EAX,DWORD[ES:EDI+EBX+NTFS_INDEX_ROOT.EntriesOffset] ; EAX = IndexHeader.EntriesOffset
ADD EAX,10H ; 10H = offsetof( NTFS_INDEX_ROOT,IndexHeader)
ADD EBX,EAX ; EBX = First IndexEntry
; 检查下一个IndexEntry
.LargeCheckNextEntry:
%IFDEF DEBUG
; MOV AL,'A'
; CALL PrintChar
; MOV EAX,DWORD[ES:EDI+EBX+NTFS_INDEX_ENTRY.IndexedFile]
; CALL PrintDword
; MOV AL,20H
; CALL PrintChar
%ENDIF
; 检查EntryFlags是否为最后一个项
MOV CX,WORD[ ES:EDI+EBX+NTFS_INDEX_ENTRY.EntryFlags]
AND CX,NTFS_INDEX_FLAG_INDEX_END
JNZ NEAR .LargeSubNode
;检查该名字是否与LoaderName相同
XOR ECX,ECX
.LargeCompareName:
; DEBUG
; MOV AL,'P'
; CALL PrintChar
; 再比较名称是否相同
MOV EDX,EDI ; SAVE EDI
MOV CL,BYTE[ ES:EDI+EBX+NTFS_INDEX_ENTRY.NameLength]
LEA EDI,[ ES:EDI+EBX+NTFS_INDEX_ENTRY.Name]
; 需要将名称变为大写
; 输入:ES:EDI = 字符串 CL = 字符串长度
CALL ToUpperCase
CMP CL,11
JB .LargeCompare
MOV CL,11 ; CL = Min( NameLength,11)
.LargeCompare:
;比较字符串
MOV ESI,LoaderName
; EDI = 待比较字符
.LargeCompareNextChar:
; MOV AL,' '
; CALL PrintChar
; MOV AX,[ESI]
; CALL PrintWord
; MOV AL,'U'
; CALL PrintChar
; MOV AX,[ES:EDI]
; CALL PrintWord
; MOV AL,' '
; CALL PrintChar
CMPSW ; ESI - EDI
JNZ .NotEqual
; 相等则继续比较
DEC CL
JNZ .LargeCompareNextChar
; CX = 0
XCHG EDI,EDX ; 恢复EDI
JMP .LargeCheckNameLength
.NotEqual:
XCHG EDI,EDX
JB .LargeSubNode ; FDOSLDR.BIN < Entry.Name
JA .LargeNextEntry
; 部分名字相同,比较相同部分长度
.LargeCheckNameLength:
CMP BYTE[ ES:EDI+EBX+NTFS_INDEX_ENTRY.NameLength],11
JZ NEAR .FoundLoader ; 找到了
JA .LargeSubNode; 该项的名字大于LoaderName,查找其子节点
.LargeNextEntry:
; 继续检查下一个项
XOR ECX,ECX
MOV CX,WORD[ES:EDI+EBX+NTFS_INDEX_ENTRY.Length]
ADD EBX,ECX ; EBX = 下一个IndexEntry
JMP .LargeCheckNextEntry
.LargeSubNode:
; DEBUG
; MOV AL,'S'
; CALL PrintChar
; EBX = 当前INDEX_ENTRY
XOR ECX,ECX
MOV CX,WORD[ES:EDI+EBX+NTFS_INDEX_ENTRY.Length] ; CX = IndexEntry.Length
ADD EBX,ECX
MOV EAX,DWORD[ES:EDI+EBX-8] ; EAX = ChildVcnLow
MOV EDX,DWORD[ES:EDI+EBX-4] ; EDX = ChildVcnHigh
MOV DWORD[ESP],EAX
MOV DWORD[ESP+4],EDX
; 根据DataRunList获得对应的LCN,然后读取该IndexBlock,再在该IndexBlock内部搜索
; 现在开始查找IndexAllocation属性
MOV EAX,NTFS_ATTRIBUTE_TYPE_INDEX_ALLOCATION
CALL FindAttribute ; EAX = Attribute相对于记录开始位置的偏移
CMP EAX,0
JZ NEAR .NotFound
; 目前不考虑有多个IndexAllocation项的复杂情况
; EAX = 属性相对于FileRecord的偏移
XOR ECX,ECX
MOV CX,WORD[ES:EDI+EAX+NTFS_NONRESIDENT_ATTRIBUTE.RunArrayOffset]
ADD EAX,ECX ; EAX = DataRun
; 对偏移进行解码,获得目标LCN
; 输入: ES:EDI = FileRecord EBX = DataRun EDX:EAX = VCN
; 输出: EDX:EAX= LCN 0代表错误
MOV EBX,EAX ; EBX = DataRun
MOV EAX,DWORD[ESP]
MOV EDX,DWORD[ESP+4]
CALL DecodeDataRunList
; 假设32位(不管高32位)
CMP EAX,00H
JZ NEAR .NotFound
;找到了Lcn 读取该IndexBlock
; 构造读取数据包
XOR ECX,ECX
MOV CL, BYTE[SectorsPerCluster]
MUL ECX ; EDX:EAX = 起始扇区号
MOV DWORD[ BP - DAP_SECTOR_HIGH ], EDX
MOV DWORD[ BP - DAP_SECTOR_LOW ], EAX
MOV WORD [ BP - DAP_BUFFER_SEG ], DATA_BUF_SEG
MOV WORD [ BP - DAP_BUFFER_OFF ], DATA_BUF_OFF
; 计算需要读取的扇区数
XOR EDX,EDX
MOV EAX,DWORD[ESP+08H] ; BytesPerIndexBlock
XOR ECX,ECX
MOV CX,WORD[BytesPerSector]
ADD EAX,ECX
DEC EAX
DIV ECX
MOV BYTE [ BP - DAP_READ_SECTORS], AL
; 读取扇区
CALL ReadSectors
; FixupRecord
; ES:EDI = FileRecord
MOV AX,DATA_BUF_SEG
MOV ES,AX
MOV EDI,DATA_BUF_OFF
CALL FixupRecord
; 获得第一个IndexEntry项
MOV EBX,00H
MOV EAX,DWORD[ES:EDI+EBX+NTFS_INDEX_BLOCK.EntriesOffset] ; EAX = IndexHeader.EntriesOffset
ADD EAX,18H ; 18H = offsetof( NTFS_INDEX_BLOCK,IndexHeader)
ADD EBX,EAX ; EBX = First IndexEntry
JMP .LargeCheckNextEntry
;====================================================================
; 不存在IndexAllocation的目录(小目录)
;====================================================================
.SmallIndexRoot:
; 只需搜索IndexRoot就可确定是否有FDOSLDR.BIN
; EBX = IndexRoot
MOV EAX,DWORD[ES:EDI+EBX+NTFS_INDEX_ROOT.EntriesOffset] ; EAX = IndexHeader.EntriesOffset
ADD EAX,10H ; 10H = offsetof( NTFS_INDEX_ROOT,IndexHeader)
ADD EBX,EAX ; EBX = First IndexEntry
; 检查下一个IndexEntry
.CheckNextEntry:
; 检查EntryFlags是否为最后一个项
MOV CX,WORD[ ES:EDI+EBX+NTFS_INDEX_ENTRY.EntryFlags]
AND CX,NTFS_INDEX_FLAG_INDEX_END
JZ .NotFound
; 首先检查名称长度 FDOSLDR.BIN
CMP BYTE[ ES:EDI+EBX+NTFS_INDEX_ENTRY.NameLength],11
JNZ .NextEntry
; 再比较名称是否相同
PUSH EDI
LEA EDI,[ ES:EDI+EBX+NTFS_INDEX_ENTRY.Name]
MOV ESI,LoaderName
MOV ECX,11
REPE CMPSW
POP EDI ; 恢复EDI
JCXZ .FoundLoader
.NextEntry:
XOR ECX,ECX
MOV CX,WORD[ES:EDI+EBX+NTFS_INDEX_ENTRY.Length]
ADD EBX,ECX ; EBX = 下一个IndexEntry
JMP .CheckNextEntry
.FoundLoader:
MOV EAX,DWORD[ES:EDI+EBX+NTFS_INDEX_ENTRY.IndexedFile] ; 低32位
MOV EDX,DWORD[ES:EDI+EBX+NTFS_INDEX_ENTRY.IndexedFile+4] ; 高32位
%IFDEF DEBUG
; CALL PrintDword
; MOV AL,20H
; CALL PrintChar
%ENDIF
JMP SHORT .End
; 没有找到
.NotFound:
MOV EAX,0
MOV EDX,0
.End:
ADD ESP,16
POP ECX
POP EBX
RET
;=============================================================
; 对偏移进行解码,获得目标LCN
;
; 输入: ES:EDI = FileRecord EBX = DataRun EDX:EAX = VCN
; 输出: EDX:EAX= LCN 0代表错误
;
;=============================================================
DecodeDataRunList:
PUSH ESI
PUSH ECX
; 临时变量
SUB ESP,40 ; ESP ESP-8 ESP-16 ESP-24 RunOffset RunLength StartLcn
MOV DWORD[ESP+00H],00H ; RunOffset
MOV DWORD[ESP+04H],00H
MOV DWORD[ESP+08H],00H ; RunLength
MOV DWORD[ESP+0CH],00H
MOV DWORD[ESP+10H],00H ; StartLcn
MOV DWORD[ESP+14H],00H
MOV DWORD[ESP+18H],00H ; StartVcn
MOV DWORD[ESP+1CH],00H
MOV DWORD[ESP+20H],EAX
MOV DWORD[ESP+28H],EDX
; 开始解码
.DecodeRun:
; 更新StartVcn
MOV EAX,DWORD[ESP+08H] ; EAX = RunLength
ADD DWORD[ESP+18H],EAX ; StartVcn += RunLenth;
; 检查是否结束
CMP BYTE[ES:EDI+EBX],00H
JZ NEAR .NotFound
; 首先解码长度和偏移所占的字节数
; DH = 长度尺寸
; DL = 偏移尺寸
MOV DL,BYTE[ES:EDI+EBX]
MOV DH,DL
AND DH,0FH
SHR DL,04H
AND DL,0FH
INC EBX ; SKIP First Byte
MOV ECX,00H
; 首先解码长度,假设不会超过4个字节(32位长度)
.NextLengthByte:
XOR EAX,EAX
MOV AL,BYTE[ES:EDI+EBX]
SHL EAX,CL
ADD DWORD[ESP+08H],EAX
INC EBX
ADD CL,08H
DEC DH
JNZ .NextLengthByte
; 解码偏移长度,假设不会超过4个字节(32位偏移)
MOV ECX,00H
.NextOffsetByte:
XOR EAX,EAX
MOV AL,BYTE[ES:EDI+EBX]
SHL EAX,CL
; 是否最后一个字节(符号位)
CMP DL,1
JA .NotLastByteOrPostive
; 是最后一个字节,需要考虑符号问题
CMP BYTE[ES:EDI+EBX],00H
JGE .NotLastByteOrPostive ; 有符号比较
; 最后一个字节小于0
NEG EAX ; 求补码
.NotLastByteOrPostive:
ADD DWORD[ESP+00H],EAX
INC EBX
ADD CL,08H
DEC DL
JNZ .NextOffsetByte
;=============================================================================
; 接下来检查当前Run是否包含待查询的VCN
;=============================================================================
MOV EAX,DWORD[ESP+00H]
ADD DWORD[ESP+10H],EAX ; StartLcn += RunOffset ; 当前Run的起始逻辑簇号
; 如果 StartVcn <= SearchVcn < StartVcn+RunLength
MOV EAX,DWORD[ESP+20H] ; Eax = SearchVcn
CMP EAX,DWORD[ESP+18H] ; StartVcn
JB .DecodeRun ; SearchVcn < StartVcn
SUB EAX,DWORD[ESP+08H] ; SearchVcn-RunLength
CMP EAX,DWORD[ESP+18H] ;
JGE .DecodeRun
; 找到咯 StartVcn <= SearchVcn < StartVcn+RunLength
; Lcn = StartLcn + ( SearchVcn - StartVcn )
;
MOV EAX,DWORD[ESP+20H] ; Eax = SearchVcn
ADD EAX,DWORD[ESP+10H] ; SearchVcn + StartLcn
SUB EAX,DWORD[ESP+18H] ; - StartVcn
MOV EDX,DWORD[ESP+28H] ; EDX不变
JMP .DecodeEnd ; 找到了
; 没有找到
.NotFound:
MOV EAX,00H
MOV EDX,00H
; 解码结束
.DecodeEnd:
; 恢复堆栈
ADD ESP,40
POP ECX
POP ESI
RET
;=============================================================
; 需要将名称变为大写
; 输入:ES:EDI = 字符串 CX = 字符串长度 (宽字符串)
;
;=============================================================
ToUpperCase:
PUSH ECX
PUSH EBX
XOR EBX,EBX
TEST CX,CX
JZ .End ; CX = 0
; 目前只处理 a-z => A-Z
.CheckNextChar:
CMP WORD[ES:EDI+EBX],0061H
JB .NextChar
CMP WORD[ES:EDI+EBX],007AH
JA .NextChar
; a < ch < z
SUB WORD[ES:EDI+EBX],20H
.NextChar:
; 继续处理下一个字符
INC EBX
INC EBX
DEC CX
JNZ .CheckNextChar
.End:
; 返回
POP EBX
POP ECX
RET
;====================================================================
;
; 显示一个字符
; 输入: AL = 待显示字符
;
;====================================================================
PrintChar:
PUSH AX
PUSH BX
MOV AH,0EH
MOV BX,7
INT 10H
POP BX
POP AX
RET
;====================================================================
;
; 显示16进制的值(将一个BYTE变为两个ASCII字符打印出来,用于调试)
; 输入: AL = 待显示的字节
;
;====================================================================
PrintByte:
PUSH BX
MOV BH,AL
; 显示高4位
SHR AL,4
AND AL,0FH
ADD AL,30H
CMP AL,39H
JLE .PrintIt
ADD AL,07H
.PrintIt:
CALL PrintChar
; 显示低4位
MOV AL,BH
AND AL,0FH
ADD AL,30H
CMP AL,39H
JLE .PrintItAgain
ADD AL,07H
.PrintItAgain:
CALL PrintChar
POP BX
RET
;====================================================================
; 显示一个WORD值
; AX = 待显示的字
;====================================================================
PrintWord:
PUSH AX
; 先显示高8位
SHR AX,8
CALL PrintByte
; 再显示低8位
POP AX
CALL PrintByte
; 返回
RET
;====================================================================
; 显示一个DWORD值
; EAX = 待显示的双字
;====================================================================
PrintDword:
PUSH EAX
; 先显示高16位
SHR EAX,16
CALL PrintWord
; 再显示低16位
POP EAX
CALL PrintWord
; 返回
RET
;====================================================================
; 扇区最后的标记字节(NASM不支持重复ORG)
;====================================================================
SecondPadding TIMES 2558-($-$$) DB 00H
SecondSignature DW 0AA55H