时间:2009-08-12 点击: 次 来源:本站原创 作者:佚名 - 小 + 大
| NTFS文件系统启动扇区代码(简化版) ;==================================================================== ; ; 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 原文网址: http://dev.csdn.net/article/78491.shtm |
上一篇:DEBUG百度百科
下一篇:FAT32引导扇区代码