This code I found in the boot section of the Windows XP source code dump checks for the ability to boot from CD during the boot process of the Windows XP installer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 | ;++ ; ;Copyright (c) 1995 Microsoft Corporation ; ;Module Name: ; ; bootfix.asm ; ;Abstract: ; ; The code in this "image" is responsible for checking if is appropriate ; for us to boot from CD. We want to boot from CD whenever we don't have ; a valid active partition or when the user pressed CTRL key during the ; boot process. ; ;Author: ; ; Calin Negreanu (calinn) 25-May-1998 ; ;Environment: ; ; Image has been loaded at 2000:0000 by ETFS boot code. ; Real mode ; ISO 9660 El Torito no-emulation CD-ROM Boot support ; DL = El Torito drive number we booted from ; ;Revision History: ; ; Calin Negreanu (calinn) 18-Feb-1999 ; ;-- page ,132 title bootfix name bootfix .8086 CODE SEGMENT ASSUME CS:CODE,DS:CODE,SS:NOTHING,ES:NOTHING ORG 0000H _BootFix label byte MaxCodeSize EQU 1024 Part_Active EQU 0 Part_Type EQU 4 Data_PartType EQU 0 ;address of partition type inside BootData structure LoadSeg EQU 3000H ;we load MBR here SectSize EQU 512 EntriesOnMbr EQU 4 MbrDataOff EQU 01BEH VolbootOrg EQU 7c00h JMPFAR MACRO DestOfs,DestSeg db 0eah dw OFFSET DestOfs dw DestSeg endm START: ; ; we already have a valid stack set by the original ETFS boot sector ; we only need to set ds and es ; push ds push es mov ax,cs mov ds,ax mov es,ax ; ; read partition table from hdd 80h at LoadSeg:0000 ; push es mov ax,LoadSeg mov es,ax mov bx,0000h mov ax,0201h ;read function, one sector mov cx,0001h mov dx,0080h int 13h jnc MbrOk ; ; there was an error, boot from CD ; pop es jmp CdBoot MbrOk: pop es ; ; now it's the time to loop and find the active partition table ; push es mov ax,LoadSeg mov es,ax mov cx,EntriesOnMbr ;number of partitions in partition table mov bp,MbrDataOff ;01beh LoopActive: ; ; 00 - inactive, 80h active, others-invalid ; cmp BYTE PTR es:[bp].Part_Active,00H jl CheckInactive jne BadMbr add bp,16 loop LoopActive ; ; no active partition found. boot from CD ; pop es jmp CdBoot CheckInactive: push bp InactiveLoop: add bp,16 dec cx jz ActiveFound cmp BYTE PTR es:[bp].Part_Active,00H je InactiveLoop pop bp BadMbr: ; ; bad mbr was found. boot from CD ; pop es jmp CdBoot ActiveFound: pop bp pop es ; ; let's see if we can display UI (that is if MsgPressKey is not empty ; mov si, OFFSET MsgPressKey lodsb mov UIAllowed, al push si mov si,OFFSET MsgPressKey call PrintMsg pop si ; ; read all available keys from the queue (prevents us from booting from CD when there ; is some garbage there ; mov cx, 80h FlushQueue: mov ah, 01h int 16h jz QueueEmpty mov ah, 00h int 16h loop FlushQueue QueueEmpty: ; ; hook int08 ; cli push es xor ax,ax mov es,ax mov bx,08h * 4 mov ax,es:[bx] mov WORD PTR [OldInt08 ], ax mov ax,es:[bx+2] mov WORD PTR [OldInt08+2], ax mov WORD PTR es:[bx],OFFSET NewInt08 mov WORD PTR es:[bx+2],cs pop es sti ; ; loop until the delay ticks is 0. Check for a key pressed (if UI), or for CTRL pressed (if no UI) ; CheckKey: cmp UIAllowed, 0 je CheckCTRL mov ah, 01h int 16h jnz KeyPressed CheckCTRL: mov ah,02h int 16h and al,00000100b jnz KeyPressed NotPressed: cmp DotTicks, 0 jg AddDot push si mov si,OFFSET MsgDot call PrintMsg pop si mov DotTicks, 18 AddDot: cmp DelayTicks, 0 jne CheckKey call UnhookInt08 jmp BootFromHD UnhookInt08: cli push es xor ax,ax mov es,ax mov bx,08h * 4 mov ax,WORD PTR [OldInt08] mov es:[bx],ax mov ax,WORD PTR [OldInt08+2] mov es:[bx+2],ax pop es sti ret KeyPressed: call UnhookInt08 jmp CdBoot BootFromHD: ; ; let's move the mbr code to 0000:7c00 and jump there ; mov ax,LoadSeg mov ds,ax xor ax,ax mov es,ax xor si,si mov di,VolBootOrg mov cx,SectSize cld rep movsb mov dl,80h JMPFAR VolbootOrg,0000H CdBoot: ; ; return to caller code ; pop es pop ds retf NewInt08: pushf cli cmp WORD PTR cs:[DelayTicks], 0 je default08 dec WORD PTR cs:[DotTicks] dec WORD PTR cs:[DelayTicks] default08: popf push WORD PTR cs:[OldInt08+2] push WORD PTR cs:[OldInt08] retf ; ;EXPECTS DS:SI - MESSAGE ADDR ; PrintMsg proc near push ax push bx cmp UIAllowed, 0 je PrintMsgEnd PrintMsgLoop: lodsb cmp al,0 je PrintMsgEnd mov ah,0eh mov bx,0007h int 10h jmp PrintMsgLoop PrintMsgEnd: pop bx pop ax ret PrintMsg endp include bootfix.inc ; message text UIAllowed db 0 MsgDot db "." db 0 OldInt08 dd ? DelayTicks dw 4*18+1 ; 4 seconds DotTicks dw 18 .errnz ($-_BootFix) GT (MaxCodeSize - 2) ;FATAL: bootfix code is too large org MaxCodeSize - 2 db 55h,0aah CODE ENDS END START |
This is interesting ASM code, this checks whether there is a valid Windows XP partition left over from the first part of the installation process.
This code sample below is the section of ASM code responsible for checking if we have a valid partition to boot from.
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | ; ; now it's the time to loop and find the active partition table ; push es mov ax,LoadSeg mov es,ax mov cx,EntriesOnMbr ;number of partitions in partition table mov bp,MbrDataOff ;01beh LoopActive: ; ; 00 - inactive, 80h active, others-invalid ; cmp BYTE PTR es:[bp].Part_Active,00H jl CheckInactive jne BadMbr add bp,16 loop LoopActive ; ; no active partition found. boot from CD ; pop es jmp CdBoot CheckInactive: push bp InactiveLoop: add bp,16 dec cx jz ActiveFound cmp BYTE PTR es:[bp].Part_Active,00H je InactiveLoop pop bp BadMbr: ; ; bad mbr was found. boot from CD ; pop es jmp CdBoot ActiveFound: pop bp pop es |
This code is very simple but effective, this is how the installer detects the valid bootable partition after completing the initial installation of Windows XP.
And the code below is the full ASM source of the boot loader for the Windows XP installer.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 | ;++ ; ;Copyright (c) 1995 Compaq Computer Corporation ; ;Module Name: ; ; etfsboot.asm ; ;Abstract: ; ; The ROM in the IBM PC starts the boot process by performing a hardware ; initialization and a verification of all external devices. If an El ; Torito CD-ROM with no-emulation support is detected, it will then load ; the "image" pointed to in the Boot Catalog. This "image" is placed at ; the physical address specified in the Boot Catalog (which should be 07C00h). ; ; The code in this "image" is responsible for locating NTLDR, loading the ; first sector of NTLDR into memory at 2000:0000, and branching to it. ; ; There are only two errors possible during execution of this code. ; 1 - NTLDR does not exist ; 2 - BIOS read error ; ; In both cases, a short message is printed, and the user is prompted to ; reboot the system. ; ; ;Author: ; ; Steve Collins (stevec) 25-Oct-1995 ; ;Environment: ; ; Image has been loaded at 7C0:0000 by BIOS. (or 0000:7C00 to support some broken BIOSes) ; Real mode ; ISO 9660 El Torito no-emulation CD-ROM Boot support ; DL = El Torito drive number we booted from ; ;Revision History: ; ; Calin Negreanu (calinn) 25-May-1998 - added safety check at the beginning of the code ; - added code for loading and executing BOOTFIX.BIN ; - modified error path ;-- page ,132 title boot - NTLDR ETFS loader name etfsboot EtfsCodeSize EQU 2048 BootSeg segment at 07c0h BootSeg ends DirSeg segment at 1000h DirSeg ends LoadSeg segment at 2000h LoadSeg ends BootCode segment ;would like to use BootSeg here, but LINK flips its lid ASSUME CS:BootCode,DS:NOTHING,ES:NOTHING,SS:NOTHING public ETFSBOOT ETFSBOOT proc far cli ;WARNING!!! DO NOT CHANGE THE STACK SETUP. BOOTFIX NEEDS THIS TO BE HERE. xor ax,ax ; Setup the stack to a known good spot mov ss,ax ; Stack is set to 0000:7c00, which is just below this code mov sp,7c00h sti mov ax,cs ; Set DS to our code segment (should be 07C0h) mov ds,ax assume DS:BootCode ; ; Save the Drive Number for later use ; push dx ; ; Let's do some safety checks here. We are going to check for three things: ; 1. We are loaded at 07c0:0000 or 0000:7C00 ; 2. Boot Drive Number looks good (80h-FFh) ; 3. Our code was completely loaded by the BIOS ; call NextInstr NextInstr: pop si ; Get IP from the stack sub si,OFFSET NextInstr ; See if we run with ORIGIN 0 jz NormalCase ; Yes cmp si,7C00h ; See if, at least we run with ORIGIN 7C00H jne BootErr$wof1 ; If not, try to display some message mov ax,cs ; If offset is 7C00H, segment should be 0 cmp ax,0000h jne BootErr$wof2 ; If not, try to display some message ; We are loaded at 0000:7C00 instead of 07C0:0000. This could mess up ; some stuff so we are going to fix it. ; hack to execute JMP 07c0:BootOK db 0eah dw OFFSET BootOK dw BootSeg NormalCase: mov MSG_BAD_BIOS_CODE, '3' mov ax,cs ; See if segment is 07C0H cmp ax,07c0h jne BootErr$wnb ; If not, try to display some message BootOK: ; ; Reset ds in case we needed to change code segment ; mov ax,cs mov ds,ax ; ; OK so far. Let's try to see if drive letter looks good (80h-FFh) ; mov MSG_BAD_BIOS_CODE, '4' cmp dl,80h jb BootErr$wnb ; ; OK so far. Let's try to see if all our code was loaded. ; We look for our signature at the end of the code. ; mov MSG_BAD_BIOS_CODE, '5' mov bx, EtfsCodeSize - 2 mov ax, WORD PTR DS:[bx] cmp ax, 0AA55h jne BootErr$wnb ; ; Finally, everything looks good. ; ; ; Save the Drive Number for later use - right now drive number is pushed on the stack ; pop dx mov DriveNum,dl ; ; Let's try to load and run BOOTFIX.BIN ; .386 push OFFSET BOOTFIXNAME push 11 push LoadSeg call LoadFile jc FindSetupLdr ; ; We have BOOTFIX.BIN loaded. We call that code to see if we should boot from CD. If we shouldn't ; we'll not come back here. ; .286 pusha push ds push es ; ; BOOTFIX requires: ; DL = INT 13 drive number we booted from ; mov dl, DriveNum ; DL = CD drive number ;hack to execute CALL LoadSeg:0000 db 9Ah dw 0000h dw LoadSeg pop es pop ds popa .8086 FindSetupldr: ; ; Scan for the presence of SETUPLDR.BIN ; .386 push OFFSET LOADERNAME push 12 push LoadSeg call LoadFile jc BootErr$bnf ; ; SETUPLDR requires: ; DL = INT 13 drive number we booted from ; mov dl, DriveNum ; DL = CD drive number xor ax,ax .386 push LoadSeg push ax retf ; "return" to NTLDR (LoadSeg:0000h). Will not come back here. ETFSBOOT endp ; ; BootErr - print error message and hang the system. ; BootErr proc BootErr$wof1: ; we were loaded at a wrong address - Code 1 PUSH SI MOV BX, SI ADD BX, OFFSET MSG_BAD_BIOS_CODE MOV BYTE PTR DS:[BX], '1' ADD SI, OFFSET MSG_BAD_BIOS JMP BootErr2 BootErr$wof2: ; we were loaded at a wrong address - Code 2 PUSH SI MOV BX, SI ADD BX, OFFSET MSG_BAD_BIOS_CODE MOV BYTE PTR DS:[BX], '2' ADD SI, OFFSET MSG_BAD_BIOS JMP BootErr2 BootErr$wnb: ; some other BIOS problem PUSH 0 MOV SI, OFFSET MSG_BAD_BIOS JMP BootErr2 BootErr$bnf: ; NTLDR not found PUSH 0 MOV SI, OFFSET MSG_NO_NTLDR JMP BootErr2 BootErr$mof: ; memory overflow PUSH 0 MOV SI, OFFSET MSG_MEM_OVERFLOW JMP BootErr2 BootErr2: CALL BootErrPrint POP SI JMP BootFromHD BootErrPrint: LODSB ; Get next character OR AL, AL JZ BEdone MOV AH, 14 ; Write teletype MOV BX, 7 ; Attribute INT 10H ; Print it JMP BootErrPrint BEdone: RET BootErr endp ; ; we are trying to boot from HD. We need to move ourself out of ; this area because we are going to load MBR here ; BootFromHD: ; ; let's wait here for two seconds, so the user gets a chance to see the message ; ; ; hook INT08 ; MOV [SI+TicksCount], 24H ; two seconds delay CLI PUSH ES XOR AX, AX MOV ES, AX MOV BX, 0020H MOV AX, ES:[BX] MOV WORD PTR [SI+OldInt08], AX MOV AX, ES:[BX+2] MOV WORD PTR [SI+OldInt08+2], AX MOV ES:[BX], SI ADD ES:[BX], OFFSET NewInt08 MOV ES:[BX+2], CS POP ES STI ; ; now let's actively wait for TicksCount to become zero ; Delay: CMP [SI+TicksCount], 0 JNE Delay ; ; unhook INT08 ; cli push es xor ax,ax mov es,ax mov bx,08h * 4 mov ax,WORD PTR [SI+OldInt08] mov es:[bx],ax mov ax,WORD PTR [SI+OldInt08+2] mov es:[bx+2],ax pop es sti ; ; now let's move ourselves away from here because we are going to load MBR here ; MoveCode: push ds push es mov ax, LoadSeg mov es, ax mov ax, cs mov ds, ax ;si is already set xor di, di mov cx, EtfsCodeSize rep movsb pop es pop ds ;hack to execute JMP LoadSeg:AfterMoveLabel db 0eah dw OFFSET AfterMoveLabel dw LoadSeg AfterMoveLabel: ; ; finally load MBR ; push es mov ax, BootSeg mov es, ax mov bx, 0000h mov ax, 0201h ;read function, one sector mov cx, 0001h mov dx, 0080h int 13h jnc MbrOk ; ; there was an error, nothing else to do ; jmp $ MbrOk: pop es ; ; now let's return into MBR code ; mov dl,80h ;hack to execute JMP 0000:7C00 db 0eah dw 7c00h dw 0000h ; ; We rely on the fact that SI is not changed when this INT occurs ; This is a pretty good assumption since this code is active only ; within the tight loop near Delay label. The odds are that some ; other IRQ occures, enables interrupts, changes SI and then INT08 ; occures. This should not happen. ; NewInt08: PUSHF CLI CMP CS:[SI+TicksCount], 0 JE Default08 DEC WORD PTR CS:[SI+TicksCount] Default08: POPF PUSH WORD PTR CS:[SI+OldInt08+2] PUSH WORD PTR CS:[SI+OldInt08] RETF include etfsboot.inc ; message text ; ; ScanForEntry - Scan for an entry in a directory ; ; Entry: ; ES:0 points to the beginning of the directory to search ; Directory length in bytes is in ExtentLen1 and Extend_Len_0 ; ; Exit: ; CF set on error, clear on success. ; ES:BX points to record containing entry if match is found ; ScanForEntry proc near mov ScanIncCount, 0 mov cx,ExtentLen0 ; CX = length of root directory in bytes (low word only) cld ; Work up for string compares xor bx,bx xor dx,dx ScanLoop: mov si, EntryToFind mov dl,byte ptr es:[bx] ; directory record length -> DL cmp dl,0 jz Skip00 ; if the "record length" assume it is "system use" and skip it mov ax,bx add ax,021h ; file identifier is at offset 21h in directory record mov di,ax ; ES:DI now points to file identifier push cx xor cx,cx mov cl,EntryLen ; compare bytes repe cmpsb pop cx jz ScanEnd ; do we have a match? CheckCountUnderFlow: ; If CX is about to underflow or be 0 we need to reset CX, ES and BX if ExtentLen1 is non-0 cmp dx,cx jae ResetCount0 sub cx,dx ; update CX to contain number of bytes left in directory cmp ScanIncCount, 1 je ScanAdd1ToCount AdjustScanPtr: ; Adjust ES:BX to point to next record add dx,bx mov bx,dx and bx,0fh push cx mov cl,4 shr dx,cl pop cx mov ax,es add ax,dx mov es,ax jmp ScanLoop Skip00: mov dx,1 ; Skip past this byte jmp CheckCountUnderFlow ScanAdd1ToCount: inc cx mov ScanIncCount,0 jmp AdjustScanPtr S0: mov ScanIncCount,1 ; We'll need to increment Count next time we get a chance jmp SetNewCount ResetCount0: cmp ExtentLen1,0 ; Do we still have at least 64K bytes left to scan? jne ResetContinue stc ; We overran the end of the directory - corrupt/invalid directory ret ResetContinue: sub ExtentLen1,1 add bx,dx ; Adjust ES:BX to point to next record - we cross seg boundary here push bx push cx mov cl,4 shr bx,cl pop cx mov ax,es add ax,bx mov es,ax pop bx and bx,0fh sub dx,cx ; Get overflow amount je S0 ; If we ended right on the boundary we need to make special adjustments dec dx SetNewCount: mov ax,0ffffh sub ax,dx ; and subtract it from 10000h mov cx,ax ; - this is the new count jmp ScanLoop ScanEnd: cmp IsDir,1 je CheckDir test byte ptr es:[bx][25],2 ; Is this a file? jnz CheckCountUnderFlow ; No - go to next record jmp CheckLen CheckDir: test byte ptr es:[bx][25],2 ; Is this a directory? jz CheckCountUnderFlow ; No - go to next record CheckLen: mov al,EntryLen cmp byte ptr es:[bx][32],al ; Is the identifier length correct? jnz CheckCountUnderFlow ; No - go to next record clc ret ScanForEntry endp ; ; ExtRead - Do an INT 13h extended read ; NOTE: I force the offset of the Transfer buffer address to be 0 ; I force the high 2 words of the Starting absolute block number to be 0 ; - This allows for a max 4 GB medium - a safe assumption for now ; ; Entry: ; Arg1 - word 0 (low word) of Number of 2048-byte blocks to transfer ; Arg2 - word 1 (high word) of Number of 2048-byte blocks to transfer ; Arg3 - segment of Transfer buffer address ; Arg4 - word 0 (low word) of Starting absolute block number ; Arg5 - word 1 of Starting absolute block number ; ; Exit ; The following are modified: ; Count0 ; Count1 ; Dest ; Source0 ; Source1 ; PartialRead ; NumBlocks ; Disk Address Packet [DiskAddPack] ; ExtRead proc near push bp ; set up stack frame so we can get args mov bp,sp push bx ; Save registers used during this routine push si push dx push ax mov bx,offset DiskAddPack ; Use BX as base to index into Disk Address Packet ; Set up constant fields mov [bx][0],byte ptr 010h ; Offset 0: Packet size = 16 bytes mov [bx][1],byte ptr 0h ; Offset 1: Reserved (must be 0) mov [bx][3],byte ptr 0h ; Offset 3: Reserved (must be 0) mov [bx][4],word ptr 0h ; Offset 4: Offset of Transfer buffer address (force 0) mov [bx][12],word ptr 0h ; Offset 12: Word 2 of Starting absolute block number (force 0) mov [bx][14],word ptr 0h ; Offset 14: Word 3 (high word) of Starting absolute block number (force 0) ; ; Initialize loop variables ; mov ax,[bp][12] ; set COUNT to number of blocks to transfer mov Count0,ax mov ax,[bp][10] mov Count1,ax mov ax,[bp][8] ; set DEST to destination segment mov Dest,ax mov ax,[bp][6] ; set SOURCE to source lbn mov Source0,ax mov ax,[bp][4] mov Source1,ax ExtReadLoop: ; ; First check if COUNT <= 32 ; cmp Count1,word ptr 0h ; Is upper word 0? jne SetupPartialRead ; No - we're trying to read at least 64K blocks (128 MB) cmp Count0,word ptr 20h ; Is lower word greater than 32? jg SetupPartialRead ; Yes - only read in 32-block increments mov PartialRead,0 ; Clear flag to indicate we are doing a full read mov ax,Count0 ; NUMBLOCKS = COUNT mov NumBlocks,al ; Since Count0 < 32 we're OK just using low byte jmp DoExtRead ; Do read SetupPartialRead: ; ; Since COUNT > 32, ; Set flag indicating we are only doing a partial read ; mov PartialRead,1 mov NumBlocks,20h ; NUMBYTES = 32 DoExtRead: ; ; Perform Extended Read ; mov al,NumBlocks ; Offset 2: Number of 2048-byte blocks to transfer mov [bx][2],al mov ax,Dest ; Offset 6: Segment of Transfer buffer address mov [bx][6],ax mov ax,Source0 ; Offset 8: Word 0 (low word) of Starting absolute block number mov [bx][8],ax mov ax,Source1 ; Offset 10: Word 1 of Starting absolute block number mov [bx][10],ax mov si,offset DiskAddPack ; Disk Address Packet in DS:SI mov ah,042h ; Function = Extended Read mov dl,DriveNum ; CD-ROM drive number int 13h ; ; Determine if we are done reading ; cmp PartialRead,1 ; Did we just do a partial read? jne ExtReadDone ; No - we're done ReadjustValues: ; ; We're not done reading yet, so ; COUNT = COUNT - 32 ; sub Count0,020h ; Subtract low-order words sbb Count1,0h ; Subtract high-order words ; ; Just read 32 blocks and have more to read ; Increment DEST to next 64K segment (this equates to adding 1000h to the segment) ; add Dest,1000h jc BootErr$mof ; Error if we overflowed ; ; SOURCE = SOURCE + 32 blocks ; add Source0,word ptr 020h ; Add low order words adc Source1,word ptr 0h ; Add high order words ; NOTE - I don't account for overflow - probably OK now since we already account for 4 GB medium ; ; jump back to top of loop to do another read ; jmp ExtReadLoop ExtReadDone: pop ax ; Restore registers used during this routine pop dx pop si pop bx mov sp,bp ; restore BP and SP pop bp ret ExtRead endp ; ; ReadExtent - Read in an extent ; ; Arg1 - segment to transfer extent to ; ; Entry: ; ExtentLen0 = word 0 (low word) of extent length in bytes ; ExtentLen1 = word 1 (high word) of extent length in bytes ; ExtentLoc0 = word 0 (low word) of starting absolute block number of extent ; ExtentLoc1 = word 1 of starting absolute block number of extent ; ; Exit: ; ExtRead exit mods ; ReadExtent proc near push bp ; set up stack frame so we can get args mov bp,sp push cx ; Save registers used during this routine push bx push ax mov cl,11 ; Convert length in bytes to 2048-byte blocks mov bx,ExtentLen1 ; Directory length = BX:AX mov ax,ExtentLen0 .386 shrd ax,bx,cl ; Shift AX, filling with BX .8086 shr bx,cl ; BX:AX = number of blocks (rounded down) test ExtentLen0,07ffh ; If any of the low-order 11 bits are set we need to round up jz ReadExtentNoRoundUp add ax,1 ; We need to round up by incrementing AX, and adc bx,0 ; adding the carry to BX ReadExtentNoRoundUp: push ax ; Word 0 (low word) of Transfer size = AX push bx ; Word 1 (high word) of Transfer size = BX .286 push [bp][4] ; Segment used to transfer extent .8086 push ExtentLoc0 ; Word 0 (low word) of Starting absolute block number push ExtentLoc1 ; Word 1 of Starting absolute block number call ExtRead add sp,10 ; Clean 5 arguments off the stack pop ax ; Restore registers used during this routine pop bx pop cx mov sp,bp ; restore BP and SP pop bp ret ReadExtent endp ; ; GetExtentInfo - Get extent location ; ; Entry: ; ES:BX points to record ; Exit: ; Location -> ExtentLoc1 and ExtentLoc0 ; Length -> ExtentLen1 and ExtentLen0 ; GetExtentInfo proc near push ax ; Save registers used during this routine mov ax,es:[bx][2] ; 32-bit LBN of extent mov ExtentLoc0,ax ; store low word mov ax,es:[bx][4] mov ExtentLoc1,ax ; store high word mov ax,es:[bx][10] ; 32-bit file length in bytes mov ExtentLen0,ax ; store low word mov ax,es:[bx][12] mov ExtentLen1,ax ; store high word pop ax ; Restore registers used during this routine ret GetExtentInfo endp LoadFile proc near push bp mov bp, sp ; ; First thing, we need to read in the Primary Volume Descriptor so we can locate the root directory ; .286 push 01h ; Word 0 (low word) of Transfer size = 1 block (2048 bytes) push 0h ; Word 1 (high word) of Transfer size = 0 push DirSeg ; Segment of Transfer buffer = DirSeg push 010h ; Word 0 (low word) of Starting absolute block number = 10h push 0h ; Word 1 of Starting absolute block number = 0 .8086 call ExtRead add sp,10 ; Clean 5 arguments off the stack ; ; Determine the root directory location LBN -> ExtentLoc1:ExtentLoc0 ; determine the root directory data length in bytes -> ExtentLen1:ExtentLen0 ; mov ax,DirSeg ; ES is set to segment used for storing PVD and directories mov es,ax ASSUME ES:DirSeg mov ax,es:[09eh] ; 32-bit LBN of extent at offset 158 in Primary Volume Descriptor mov ExtentLoc0,ax ; store low word mov ax,es:[0a0h] mov ExtentLoc1,ax ; store high word mov ax,es:[0a6h] ; 32-bit Root directory data length in bytes at offset 166 in Primary Volume Descriptor mov ExtentLen0,ax ; store low word mov ax,es:[0a8h] mov ExtentLen1,ax ; store high word ; ; Now read in the root directory ; .286 push DirSeg ; Segment used for transfer = DirSeg .8086 call ReadExtent add sp,2 ; Clean 1 argument off the stack ; ; Scan for the presence of the I386 directory ; ES points to directory segment ; mov EntryToFind, offset I386DIRNAME mov EntryLen,4 mov IsDir,1 call ScanForEntry jc EntryNotFound ; ; We found the I386 directory entry, so now get its extent location (offset -31 from filename ID) ; ES:[BX] still points to the directory record for the I386 directory ; call GetExtentInfo ; ; Now read in the I386 directory ; .286 push DirSeg ; Segment used for transfer = DirSeg .8086 call ReadExtent add sp,2 ; Clean 1 argument off the stack ; ; Scan for the presence of the file that we need ; ES points to directory segment ; mov ax, DirSeg mov es, ax mov ax, [bp][8] mov EntryToFind, ax mov al, [bp][6] mov EntryLen, al mov IsDir,0 call ScanForEntry jc EntryNotFound ; ; We found the needed file, so now get its extent location (offset -31 from filename ID) ; ES:[BX] still points to the directory record for that code ; call GetExtentInfo ; ; Now, go read the file ; .286 push [bp][4] ; Segment used for transfer .8086 call ReadExtent add sp,2 ; Clean 1 argument off the stack EntryNotFound: pop bp ret LoadFile endp OldInt08 DD ? ; Default Int08 vector TicksCount dw 24H ; two seconds DiskAddPack db 16 dup (?) ; Disk Address Packet PartialRead db 0 ; Boolean indicating whether or not we are doing a partial read LOADERNAME db "SETUPLDR.BIN" BOOTFIXNAME db "BOOTFIX.BIN" I386DIRNAME db "I386" DriveNum db ? ; Drive number used for INT 13h extended reads ExtentLoc0 dw ? ; Loader LBN - low word ExtentLoc1 dw ? ; Loader LBN - high word ExtentLen0 dw ? ; Loader Length - low word ExtentLen1 dw ? ; Loader Length - high word Count0 dw ? ; Read Count - low word Count1 dw ? ; Read Count - high word Dest dw ? ; Read Destination segment Source0 dw ? ; Read Source - word 0 (low word) Source1 dw ? ; Read Source - word 1 NumBlocks db ? ; Number of blocks to Read EntryToFind dw ? ; Offset of string trying to match in ScanForEntry EntryLen db ? ; Length in bytes of entry to match in ScanForEntry IsDir db ? ; Boolean indicating whether or not entry to match in ScanForEntry is a directory ScanIncCount db ? ; Boolean indicating if we need to add 1 to Count after adjustment in ScanForEntry .errnz ($-ETFSBOOT) GT (EtfsCodeSize - 2) ; FATAL PROBLEM: boot sector is too large org (EtfsCodeSize - 2) db 55h,0aah BootSectorEnd label dword BootCode ends END ETFSBOOT |
This code sample from the above will read the current root directory for installation.
729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 | ; ; First thing, we need to read in the Primary Volume Descriptor so we can locate the root directory ; .286 push 01h ; Word 0 (low word) of Transfer size = 1 block (2048 bytes) push 0h ; Word 1 (high word) of Transfer size = 0 push DirSeg ; Segment of Transfer buffer = DirSeg push 010h ; Word 0 (low word) of Starting absolute block number = 10h push 0h ; Word 1 of Starting absolute block number = 0 .8086 call ExtRead add sp,10 ; Clean 5 arguments off the stack ; ; Determine the root directory location LBN -> ExtentLoc1:ExtentLoc0 ; determine the root directory data length in bytes -> ExtentLen1:ExtentLen0 ; mov ax,DirSeg ; ES is set to segment used for storing PVD and directories mov es,ax ASSUME ES:DirSeg mov ax,es:[09eh] ; 32-bit LBN of extent at offset 158 in Primary Volume Descriptor mov ExtentLoc0,ax ; store low word mov ax,es:[0a0h] mov ExtentLoc1,ax ; store high word mov ax,es:[0a6h] ; 32-bit Root directory data length in bytes at offset 166 in Primary Volume Descriptor mov ExtentLen0,ax ; store low word mov ax,es:[0a8h] mov ExtentLen1,ax ; store high word ; ; Now read in the root directory ; .286 push DirSeg ; Segment used for transfer = DirSeg .8086 call ReadExtent add sp,2 ; Clean 1 argument off the stack ; ; Scan for the presence of the I386 directory ; ES points to directory segment ; mov EntryToFind, offset I386DIRNAME mov EntryLen,4 mov IsDir,1 call ScanForEntry jc EntryNotFound ; ; We found the I386 directory entry, so now get its extent location (offset -31 from filename ID) ; ES:[BX] still points to the directory record for the I386 directory ; call GetExtentInfo ; ; Now read in the I386 directory ; .286 push DirSeg ; Segment used for transfer = DirSeg .8086 call ReadExtent add sp,2 ; Clean 1 argument off the stack ; ; Scan for the presence of the file that we need ; ES points to directory segment ; mov ax, DirSeg mov es, ax mov ax, [bp][8] mov EntryToFind, ax mov al, [bp][6] mov EntryLen, al mov IsDir,0 call ScanForEntry jc EntryNotFound ; ; We found the needed file, so now get its extent location (offset -31 from filename ID) ; ES:[BX] still points to the directory record for that code ; call GetExtentInfo ; ; Now, go read the file ; .286 push [bp][4] ; Segment used for transfer .8086 call ReadExtent add sp,2 ; Clean 1 argument off the stack EntryNotFound: pop bp ret LoadFile endp |
So, this is how the Windows XP installation CD boots, this is a very interesting insight into the workings of this technology.