1/* 2 * PROJECT: ReactOS Bootsector 3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) 4 * FILE: boot/freeldr/bootsect/fat32.S 5 * PURPOSE: Implementing boot sector for NTFS 6 * COPYRIGHT: Copyright 2020 Sylvain Deverre (deverre.sylv@gmail.com) 7 */ 8 9/* INCLUDES ******************************************************************/ 10 11#include <asm.inc> 12#include <freeldr/include/arch/pc/x86common.h> 13 14.code16 15 16//ORG HEX(7c00) 17 18start: 19 jmp short main 20 nop 21OEMName: 22 .ASCII "NTFS " // NTFS signature 23BytesPerSector: 24 .word 512 25SectsPerCluster: 26 .byte 8 27ReservedSectors: 28 .word 0 // Unused by NTFS 29NumberOfFats: 30 .byte 0 // Unused by NTFS 31MaxRootEntries: 32 .word 0 // Unused by NTFS 33TotalSectors: 34 .word 0 // Unused by NTFS 35MediaDescriptor: 36 .byte HEX(0f8) 37SectorsPerFat: 38 .word 0 // Unused by NTFS 39SectorsPerTrack: 40 .word 0 // Should contain disk geometry 41NumberOfHeads: 42 .word 0 // Should contain disk geometry 43HiddenSectors: 44 .long 0 // Filled by format program 45TotalSectorsBig: 46 .long 0 // Unused by NTFS 47// NTFS inserted info 48BootDrive: 49 .byte HEX(80) 50CurrentHead: 51 .byte 0 52BootSignature: 53 .byte HEX(80) 54Unused: 55 .byte 0 56VolumeSectorCount: 57 .quad 0 // Must be patched by format program ! 58MftLocation: 59 .quad 0 // Must be patched by format program ! 60MftMirrorLocation: 61 .quad 0 // Must be patched by format program ! 62ClustersPerMftRecord: 63 .long 0 64ClustersPerIndexRecord: 65 .long 0 66VolumeSerialNumber: 67 .quad 0 68Checksum: 69 .long 0 70 71main: 72 xor ax,ax // Setup segment registers 73 mov ds,ax // Make DS correct 74 mov es,ax // Make ES correct 75 mov ss,ax // Make SS correct 76 mov sp, HEX(7c00) 77 mov bp, sp // Setup a stack 78 79 cmp byte ptr [BootDrive], HEX(0ff) // If they have specified a boot drive then use it 80 jne GetDriveParameters 81 82 mov byte ptr [BootDrive], dl // Save the boot drive 83 84GetDriveParameters: 85 mov ah, 8 86 mov dl, byte ptr [BootDrive] // Get boot drive in dl 87 int HEX(13) // Request drive parameters from the bios 88 jnc CalcDriveSize // If the call succeeded then calculate the drive size 89 90 // If we get here then the call to the BIOS failed 91 // so just set CHS equal to the maximum addressable 92 // size 93 mov cx, HEX(0ffff) 94 mov dh, cl 95 96CalcDriveSize: 97 // Now that we have the drive geometry 98 // lets calculate the drive size 99 mov bl, ch // Put the low 8-bits of the cylinder count into BL 100 mov bh, cl // Put the high 2-bits in BH 101 shr bh, 6 // Shift them into position, now BX contains the cylinder count 102 and cl, HEX(3f) // Mask off cylinder bits from sector count 103 // CL now contains sectors per track and DH contains head count 104 movzx eax, dh // Move the heads into EAX 105 movzx ebx, bx // Move the cylinders into EBX 106 movzx ecx, cl // Move the sectors per track into ECX 107 inc eax // Make it one based because the bios returns it zero based 108 inc ebx // Make the cylinder count one based also 109 mul ecx // Multiply heads with the sectors per track, result in edx:eax 110 mul ebx // Multiply the cylinders with (heads * sectors) [stored in edx:eax already] 111 112 // We now have the total number of sectors as reported 113 // by the bios in eax, so store it in our variable 114 mov dword ptr ds:[BiosCHSDriveSize], eax 115 116LoadExtraBootCode: 117 // First we have to load our extra boot code at 118 // next sector into memory at [0000:7e00h] 119 mov eax, HEX(1) 120 xor edx, edx 121 mov cx, 4 122 xor bx, bx 123 mov es, bx // Read sector to [0000:7e00h] 124 mov bx, HEX(7e00) 125 call ReadSectors 126 jmp StartSearch 127 128 129// Reads logical sectors into [ES:BX] 130// EDX:EAX has logical sector number to read 131// CX has number of sectors to read 132ReadSectors: 133 push es 134 add eax, dword ptr [HiddenSectors] // Add offset from the disk beginning 135 test edx, edx 136 jnz ReadSectorsLBA 137 cmp eax, dword ptr ds:[BiosCHSDriveSize] // Check if they are reading a sector outside CHS range 138 jae ReadSectorsLBA // Yes - go to the LBA routine 139 // If at all possible we want to use LBA routines because 140 // They are optimized to read more than 1 sector per read 141 142 pushad // Save logical sector number & sector count 143 144CheckInt13hExtensions: // Now check if this computer supports extended reads 145 mov ah, HEX(41) // AH = 41h 146 mov bx, HEX(55aa) // BX = 55AAh 147 mov dl, byte ptr [BootDrive] // DL = drive (80h-FFh) 148 int HEX(13) // IBM/MS INT 13 Extensions - INSTALLATION CHECK 149 jc ReadSectorsCHS // CF set on error (extensions not supported) 150 cmp bx, HEX(0aa55) // BX = AA55h if installed 151 jne ReadSectorsCHS 152 test cl,1 // CX = API subset support bitmap 153 jz ReadSectorsCHS // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported 154 155 popad // Restore sector count & logical sector number 156 157ReadSectorsLBA: 158 pushad // Save logical sector number & sector count 159 160 cmp cx, 64 // Since the LBA calls only support 0x7F sectors at a time we will limit ourselves to 64 161 jbe ReadSectorsSetupDiskAddressPacket // If we are reading less than 65 sectors then just do the read 162 mov cx, 64 // Otherwise read only 64 sectors on this loop iteration 163 164ReadSectorsSetupDiskAddressPacket: 165 mov word ptr ds:[LBASectorsRead],cx 166 push edx 167 push eax // Put 64-bit logical block address on stack 168 push es // Put transfer segment on stack 169 push bx // Put transfer offset on stack 170 push cx // Set transfer count 171 push 16 // Set size of packet to 10h 172 mov si, sp // Setup disk address packet on stack 173 174 mov dl, byte ptr [BootDrive] // Drive number 175 mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read 176 int HEX(13) // Call BIOS 177 jc PrintDiskError // If the read failed then abort 178 179 add sp, 16 // Remove disk address packet from stack 180 181 popad // Restore sector count & logical sector number 182 183 push bx 184 mov ebx, dword ptr ds:[LBASectorsRead] 185 add eax, ebx // Increment sector to read 186 adc edx, 0 187 shl ebx, 5 188 mov dx, es 189 add dx, bx // Setup read buffer for next sector 190 mov es, dx 191 pop bx 192 193 sub cx, word ptr ds:[LBASectorsRead] 194 jnz ReadSectorsLBA // Read next sector 195 196 pop es 197 ret 198 199LBASectorsRead: 200 .long 0 201 202 203// Reads logical sectors into [ES:BX] 204// EAX has logical sector number to read 205// CX has number of sectors to read 206ReadSectorsCHS: 207 popad // Get logical sector number & sector count off stack 208 209ReadSectorsCHSLoop: 210 pushad 211 xor edx, edx 212 movzx ecx, word ptr [SectorsPerTrack] 213 div ecx // Divide logical by SectorsPerTrack 214 inc dl // Sectors numbering starts at 1 not 0 215 mov cl, dl // Sector in CL 216 mov edx, eax 217 shr edx, 16 218 div word ptr [NumberOfHeads] // Divide logical by number of heads 219 mov dh, dl // Head in DH 220 mov dl, byte ptr [BootDrive] // Drive number in DL 221 mov ch, al // Cylinder in CX 222 ror ah, 2 // Low 8 bits of cylinder in CH, high 2 bits 223 // in CL shifted to bits 6 & 7 224 or cl, ah // Or with sector number 225 mov ax, HEX(0201) 226 int HEX(13) // DISK - READ SECTORS INTO MEMORY 227 // AL = number of sectors to read, CH = track, CL = sector 228 // DH = head, DL = drive, ES:BX -> buffer to fill 229 // Return: CF set on error, AH = status (see AH=01h), AL = number of sectors read 230 231 jc PrintDiskError // If the read failed then abort 232 233 popad 234 235 inc eax // Increment Sector to Read 236 237 mov dx, es 238 add dx, 32 // Increment read buffer for next sector 239 mov es, dx 240 241 loop ReadSectorsCHSLoop // Read next sector 242 243 pop es 244 ret 245 246// Displays a disk error message 247// And reboots 248PrintDiskError: 249 mov si, offset msgDiskError // Bad boot disk message 250 call PutChars // Display it 251 252 jmp Reboot 253 254// Displays a file system error message 255// And reboots 256PrintFileSystemError: 257 mov si, offset msgFileSystemError // FreeLdr not found message 258 call PutChars // Display it 259 260Reboot: 261 mov si, offset msgAnyKey // Press any key message 262 call PutChars // Display it 263 xor ax, ax 264 int HEX(16) // Wait for a keypress 265 int HEX(19) // Reboot 266 267PutChars: 268 lodsb 269 or al, al 270 jz short Done 271 mov ah, HEX(0e) 272 mov bx, 7 273 int HEX(10) 274 jmp short PutChars 275Done: 276 ret 277 278msgDiskError: 279 .ascii "Disk error", CR, LF, NUL 280msgFileSystemError: 281 .ascii "File system error", CR, LF, NUL 282msgAnyKey: 283 .ascii "Press any key to restart", CR, LF, NUL 284 285SectsPerMFT: 286 .word 0 287 288MFTStartSector: 289 .quad 0 290 291BiosCHSDriveSize: 292 .long 0 293 294.org 509 // Pad to 509 bytes 295BootPartition: 296 .byte 1 297 298BootFlagSignature: 299 .word HEX(0aa55) // BootSector signature 300 301// End of Bootsector 302// Next sector starts as sector 2, since boot sector is 303// as a file under NTFS ($Boot, which has inode number 7) 304// and takes the two first clusters of the volume (which means 305// 16 sectors, checked on Windows and mkfs.ntfs from ntfsutils) 306 307#define ATTRIBUTE_DATA HEX(80) 308#define ATTRIBUTE_INDEX_ROOT HEX(90) 309#define ATTRIBUTE_INDEX_ALLOCATION HEX(A0) 310#define FILE_MAGIC HEX(454c4946) 311#define INDX_MAGIC HEX(58444e49) 312#define NTFS_FILE_ROOT 5 313StartSearch: 314 // Compute MFT start sector 315 mov eax, dword ptr [MftLocation] 316 mov cl, byte ptr [SectsPerCluster] 317 movzx ecx, cx 318 mul ecx 319 mov dword ptr [MFTStartSector], eax 320 321 // Compute size of MFT entry in sectors 322 xor ax, ax 323 mov al, byte ptr [ClustersPerMftRecord] 324 test al, al 325 js NegativeOffset 326 mov cx, word ptr [SectsPerCluster] 327 mul cx 328 mov word ptr [SectsPerMFT], cx 329 jmp SearchNext 330 NegativeOffset: 331 // ClustersPerMftRecord is negative, so we need to perform 1 << (-ClustersPerMftRecord) 332 // to get the number of bytes needed for a MFT entry 333 not al 334 inc al 335 mov cl, al 336 xor ax, ax 337 inc ax 338 shl ax, cl 339 340 // But here want to store sectors, so we need to divide by BytesPerSector 341 xor dx, dx 342 mov cx, word ptr [BytesPerSector] 343 div cx 344 mov word ptr [SectsPerMFT], ax 345 346 SearchNext: 347 mov bp, sp 348 sub sp, HEX(10) 349 350 // Read Root Directory MFT into [2000:0] 351 mov ax, HEX(2000) 352 mov es, ax 353 xor bx, bx 354 xor edx, edx 355 mov eax, NTFS_FILE_ROOT 356 call ReadInode 357 358 // Finds freeldr.sys into index root's B-Tree 359 // and return its MFT index 360 xor ax, ax 361 call ExploreIndexRoot 362 363 // Read the MFT entry of freeldr.sys into [A00:0] 364 push eax 365 mov ax, HEX(A00) 366 mov es, ax 367 pop eax 368 call ReadInode 369 370 xor ax, ax 371 mov ebx, HEX(30) 372 call FindAttributeHdr 373 mov si, ax 374 // Move to the attribute data 375 add si, word ptr es:[si + HEX(14)] 376 377 // We don't support compressed, sparse or encrypted Freeldr 378 test dword ptr es:[si + HEX(38)], HEX(4a00) 379 jnz CompressedFreeldr 380 381 // Compute size in clusters 382 mov eax, dword ptr es:[si + HEX(28)] 383 mov cl, byte ptr [SectsPerCluster] 384 xor edx, edx 385 div ecx 386 mov cx, word ptr [BytesPerSector] 387 movzx ecx, cx 388 xor edx, edx 389 div ecx 390 mov edx, eax 391 392 push ax 393 mov si, offset msgLoading 394 call PutChars 395 pop ax 396 397 xor ax, ax 398 mov ebx, HEX(80) 399 call FindAttributeHdr 400 mov si, ax 401 add ax, word ptr es:[si + HEX(20)] 402 xor ecx, ecx 403 xor bx, bx 404 push FREELDR_BASE / 16 405 pop fs 406 FreeLdrLoad: 407 pushad 408 call ReadNonResidentAttribute 409 popad 410 mov bx, fs 411 add bx, HEX(100) 412 mov fs, bx 413 xor bx, bx 414 inc ecx 415 cmp ecx, edx 416 jbe FreeLdrLoad 417 418 mov dl, byte ptr [BootDrive] 419 mov dh, byte ptr [BootPartition] 420 ljmp16 0, FREELDR_BASE 421 422// Error message if Freeldr is compressed, encrypted or sparse 423CompressedFreeldr: 424 mov si, offset msgFreeldrCompressed 425 call PutChars 426 jmp Reboot 427 428// Finds Freeldr.sys into the directory tree and returns the 429// inode index 430// INPUT: 431// - ES:[AX] address of the MFT record 432// OUTPUT: 433// - EDX:EAX MFT number of the found file 434ExploreIndexRoot: 435 #define fileRecordBase 2 436 #define fileRecord 4 437 #define indexRootData 6 438 #define allocationRunList 8 439 440 push bp 441 mov bp, sp 442 push bx 443 push si 444 push di 445 sub sp, HEX(10) 446 mov word ptr [bp - fileRecordBase], es 447 mov word ptr [bp - fileRecord], ax 448 449 mov ebx, ATTRIBUTE_INDEX_ROOT 450 call FindAttributeHdr 451 test ax, ax 452 jz PrintFileSystemError // fail if no INDEX_ROOT 453 454 mov si, ax 455 cmp byte ptr es:[si + 8], HEX(0) 456 jnz PrintFileSystemError // fail if attribute is non-resident 457 458 add si, word ptr es:[si + HEX(14)] 459 cmp dword ptr es:[si], HEX(30) 460 jnz PrintFileSystemError // fail if FILE_NAME attribute isn't indexed 461 462 mov word ptr [bp - indexRootData], si 463 mov ax, word ptr [bp - fileRecord] 464 465 test byte ptr es:[si + HEX(0c)], 1 466 jz ExploreNext // Skip index allocation lookup if we don't have children 467 468 mov ebx, ATTRIBUTE_INDEX_ALLOCATION 469 call FindAttributeHdr 470 test ax, ax 471 jz PrintFileSystemError // No INDEX_ALLOCATION found 472 473 mov si, ax 474 cmp byte ptr es:[si + 8], 1 475 jnz PrintFileSystemError // Fail if attribute is resident (shouldn't be) 476 477 add si, word ptr es:[si + HEX(20)] 478 mov word ptr [bp - allocationRunList], si // save run list 479 480 ExploreNext: 481 mov si, word ptr [bp - indexRootData] 482 lea si, [si + 32] // get the first INDEX_ENTRY 483 484 // Main search loop. We browse the B-Tree which contains directory entries 485 // SI contains the current index entry. 486 NodeCheck: 487 test word ptr es:[si + HEX(0C)], 2 488 jnz NodeCheckLastNode 489 490 mov cl, byte ptr es:[si + HEX(50)] 491 movzx cx, cl 492 lea si, [si + HEX(52)] 493 mov di, offset FreeLdr 494 call CompareWideStrInsensitive 495 lea si, [si - HEX(52)] 496 test ax, ax 497 jz RootIndexFound 498 test word ptr es:[si + HEX(0C)], 1 499 jz ContinueSearch 500 501 test ax, HEX(f000) 502 jnz LookupChildNode // if result < 0 then explore child node 503 504 ContinueSearch: 505 add si, word ptr es:[si + 8] 506 jmp NodeCheck 507 508 RootIndexFound: 509 mov eax, dword ptr es:[si] // We found root entry, return with its MFT number 510 mov dx, word ptr es:[si + 4] // Return high part into edx. 511 // We take only the first word, the high word contains the 512 // sequence number 513 movzx edx, dx 514 jmp ExitIndexTree 515 516 NodeCheckLastNode: 517 test word ptr es:[si + HEX(0C)], 1 518 jz PrintFreeldrError 519 520 LookupChildNode: 521 // Take the right LCN at the end of the index record 522 add si, word ptr es:[si + 8] 523 mov ecx, dword ptr es:[si - 8] 524 525 // Read the fixed up LCN 526 mov bx, word ptr [bp - allocationRunList] 527 mov ax, word ptr [bp - fileRecordBase] 528 mov es, ax 529 mov ax, HEX(9000) 530 mov fs, ax 531 mov ax, bx 532 xor bx, bx 533 call ReadINDX 534 535 // Go again to the loop but with the child node list 536 mov ax, HEX(9000) 537 mov es, ax 538 mov si, 0 539 add si, es:[si + HEX(18)] // Go to the first node 540 lea si, [si + HEX(18)] 541 jmp NodeCheck 542 543 ExitIndexTree: 544 pop di 545 pop si 546 mov sp, bp 547 pop bp 548 ret 549 550// 64-bit multiplication 551// EDX:EAX operand 552// ECX operator 553Multiply64: 554 push bp 555 mov bp, sp 556 sub sp, 12 557 // Save the high part of the multiplication 558 mov dword ptr [bp - 4], edx 559 560 // Multiply the low part and save the result 561 mul ecx 562 mov dword ptr[bp - 8], eax 563 mov dword ptr[bp - 12], edx 564 565 // Multiply the high part and add the carry 566 mov eax, dword ptr [bp - 4] 567 mul ecx 568 add eax, dword ptr [bp - 12] 569 570 // Format correctly the number 571 mov edx, eax 572 mov eax, dword ptr [bp - 8] 573 mov sp, bp 574 pop bp 575 ret 576 577// Compare case-insensitive strings 578// [ES:SI] - the source file name 579// [DS:DI] - the destination file name 580// CX - compare length 581CompareWideStrInsensitive: 582 push bx 583 push si 584 push di 585 movzx cx, cl 586 CmpLoop: 587 mov ax, word ptr es:[si] 588 cmp ax, 'a' 589 jl NoUpper 590 cmp ax, 'z' 591 jg NoUpper 592 sub ax, HEX(20) 593 NoUpper: 594 mov bx, word ptr ds:[di] 595 sub bx, ax 596 test bx, bx 597 jnz CompareFail 598 add si, 2 599 add di, 2 600 dec cx 601 jnz CmpLoop 602 CompareFail: 603 mov ax, bx 604 pop di 605 pop si 606 pop bx 607 ret 608 609// Reads a NTFS cluster 610// INPUT: 611// - EDX:EAX : cluster number to read 612// OUTPUT: 613// - ES:BX : address to read 614ReadCluster: 615 push ecx 616 mov cl, byte ptr [SectsPerCluster] // Convert clusters to sectors 617 movzx ecx, cx 618 call Multiply64 619 call ReadSectors 620 pop ecx 621 ret 622 623// Reads a MFT entry 624// INPUT: 625// - EDX:EAX : MFT inode 626// OUTPUT: 627// - ES:[BX] : address to read 628ReadInode: 629 push ecx 630 push si 631 push di 632 633 push edx 634 mov cx, word ptr [SectsPerMFT] // Get the correct number of sectors for the FILE entry 635 movzx ecx, cx 636 mul ecx 637 movzx eax, ax 638 add eax, dword ptr [MFTStartSector] // Add it to the start of the MFT 639 mov cx, word ptr [SectsPerMFT] 640 movzx ecx, cx 641 pop edx 642 call ReadSectors 643 644 cmp dword ptr es:[bx], FILE_MAGIC // Ensure we get a valid FILE record 645 jnz PrintFileSystemError 646 647 call ApplyFixups 648 649 pop di 650 pop si 651 pop ecx 652 ret 653 654#define UpdateSequenceOffset 4 655#define UpdateSequenceLen 6 656// Apply fixups to INDX and FILE records 657// INPUT: 658// - ES:[BX] - pointer to the record to fixup 659ApplyFixups: 660 push si 661 push di 662 mov si, bx 663 add si, word ptr es:[bx + UpdateSequenceOffset] 664 xor cx, cx 665 inc cx 666 FixupLoop: 667 cmp cx, word ptr es:[bx + UpdateSequenceLen] 668 jz EndFixupLoop 669 mov si, bx 670 add si, word ptr es:[bx + UpdateSequenceOffset] 671 mov ax, word ptr es:[si] // Get first fixup value 672 673 mov di, cx 674 shl di, 9 675 add di, bx 676 sub di, 2 677 cmp ax, word ptr es:[di] // Check fixup value 678 jnz PrintFileSystemError // Fixup is corrupted, so print error 679 inc cx 680 add si, cx 681 mov ax, word ptr es:[si] // Apply fixup 682 mov word ptr es:[di], ax 683 684 jmp FixupLoop 685 EndFixupLoop: 686 687 pop di 688 pop si 689 ret 690 691// Reads a non-resident attribute 692// INPUT: 693// - ES:[AX] : Address of the data runs 694// - ECX : LCN to read 695// OUTPUT: 696// - FS:[BX] : Address to write to 697ReadNonResidentAttribute: 698 #define currentLCN 4 699 #define offsetWrite 8 700 #define startReadLCN HEX(0C) 701 push bp 702 mov bp, sp 703 sub sp, HEX(10) 704 push edx 705 push si 706 mov dword ptr [bp - currentLCN], 0 // Store the current LCN 707 mov word ptr [bp - offsetWrite], bx 708 mov dword ptr [bp - startReadLCN], ecx 709 mov si, ax 710 xor edx, edx 711 RunLoop: 712 mov al, byte ptr es:[si] 713 test al, al 714 jz NotFound 715 call UnpackRun 716 add dword ptr [bp - currentLCN], eax 717 cmp dword ptr [bp - startReadLCN], ecx 718 jb FoundRun 719 sub dword ptr [bp - startReadLCN], ecx // Decrement the cluster 720 jmp RunLoop 721 FoundRun: 722 mov ebx, dword ptr [bp - currentLCN] 723 mov ecx, dword ptr [bp - startReadLCN] 724 add ebx, ecx 725 726 push es 727 mov ax, fs 728 mov es, ax 729 mov eax, ebx 730 mov bx, word ptr [bp - offsetWrite] 731 call ReadCluster 732 pop es 733 jmp RunSearchEnd 734 NotFound: 735 xor ax, ax 736 RunSearchEnd: 737 pop si 738 pop edx 739 mov sp, bp 740 pop bp 741 ret 742 743// Decodes a run in the runlist 744// INPUT: 745// - ES:[SI] : address of the run 746// OUTPUT: 747// - EAX : Unpacked LCN 748// - ECX : Unpacked run length (in sectors) 749// - SI : Next run in the run list 750UnpackRun: 751 push bp 752 mov bp, sp 753 sub sp, HEX(10) 754 push ebx 755 756 // Unpack run header 757 mov bl, byte ptr es:[si] 758 inc si 759 760 mov bh, bl 761 shr bh, 4 762 and bl, 7 763 mov byte ptr [bp-2], bh // BH contains the LCN length 764 mov byte ptr [bp-1], bl // BL contains the number of cluster length 765 766 mov al, bl 767 call UnpackLen 768 mov dword ptr [bp - 8], ebx 769 770 mov al, byte ptr [bp-2] 771 call UnpackLen 772 mov cl, byte ptr es:[si-1] // Fixup sign if last byte is > 255 773 test cl, HEX(80) 774 jz NoSign 775 not eax 776 add ebx, eax 777 778 NoSign: 779 mov eax, ebx 780 mov ecx, dword ptr [bp - 8] 781 pop ebx 782 mov sp, bp 783 pop bp 784 ret 785 786 787// Auxiliary function that unpacks n bytes in the memory 788// INPUT: 789// - AL : size to unpack (max 4 bytes) 790// OUTPUT: 791// - EAX : the mask used to unpack (for negative number fixup) 792// - EBX : the unpacked number 793// - SI : Next byte to read 794UnpackLen: 795 push cx 796 movzx ax, al 797 798 // Read the whole DWORD and then compute a mask to remove 799 // unneeded bytes to get correct size 800 xor ebx, ebx 801 mov ebx, dword ptr es:[si] 802 add si, ax 803 804 cmp al, 4 805 jnz UnpackLen_not4 806 xor eax, eax 807 dec eax 808 jmp UnpackLen_mask 809 810UnpackLen_not4: 811 mov cl, al // Compute mask (2^(8*len) - 1) 812 shl cl, 3 813 xor eax, eax 814 inc eax 815 shl eax, cl 816 dec eax 817 818UnpackLen_mask: 819 and ebx, eax // Apply mask 820 pop cx 821 ret 822 823// Reads an INDX sector and applies fixups 824// INPUT: 825// - ES:[AX] : Address of the data runs 826// - ECX : LCN to read 827// OUTPUT: 828// - FS:[BX] : Address to write to 829ReadINDX: 830 push es 831 push bx 832 call ReadNonResidentAttribute 833 test ax, ax 834 jz PrintFileSystemError 835 cmp dword ptr fs:[0], INDX_MAGIC 836 jnz PrintFileSystemError // jump if not valid 837 pop bx 838 839 mov ax, fs 840 mov es, ax 841 call ApplyFixups 842 pop es 843 ret 844 845// Finds an attribute header into the MFT 846// INPUT: 847// - ES:[AX] : pointer to the MFT entry 848// - EBX : type to find 849// OUTPUT: 850// - ES:[AX] : Pointer to the attribute header in the MFT entry 851FindAttributeHdr: 852 push cx 853 push si 854 push edx 855 mov si, ax 856 mov cx, word ptr es:[si+HEX(14)] // Get offset attribute 857 add si, cx 858 FindAttributeHdrLoop: 859 mov edx, dword ptr es:[si] // Get attribute type 860 cmp edx, ebx 861 jz AttrFound 862 cmp edx, HEX(ffffffff) 863 jz AttrNotFound 864 add cx, word ptr es:[si + 4] // Add size of the attribute 865 add si, word ptr es:[si + 4] 866 jmp FindAttributeHdrLoop 867 868 AttrNotFound: 869 // Attribute not found, reset the offset 870 xor cx, cx 871 AttrFound: 872 mov ax, cx 873 pop edx 874 pop si 875 pop cx 876 ret 877 878PrintFreeldrError: 879 mov si, offset msgFreeldr 880 call PutChars 881 jmp Reboot 882 883FreeLdr: 884 .word 'F', 'R', 'E', 'E', 'L', 'D', 'R', '.', 'S', 'Y', 'S' 885msgFreeldr: 886 .ascii "FreeLdr not found, cannot boot", CR, LF, NUL 887msgLoading: 888 .ascii "Loading FreeLoader...", CR, LF, NUL 889msgFreeldrCompressed: 890 .ascii "freeldr.sys is a sparse, compressed or encrypted file, cannot boot", CR, LF, NUL 891.endcode16 892 893END 894