分享
 
 
 

NTFS文件系统启动扇区代码(简化版)

王朝other·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

;====================================================================

;

; 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

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有