[Source Code] Windows XP Home/Professional combined CD Boot Loader

From: wsw (spamtrap_at_crayne.org)
Date: 07/20/04


Date: Tue, 20 Jul 2004 21:47:04 +0000 (UTC)


;=================================================================================
;=================================================================================
;=================================================================================
;
;[Source Code] Windows XP Home/Professional combined CD Boot Loader
;
;Disassembler: Source v7.0
;
;Tools: Turbo Assembler 4.1
; Turbo Linker Version 7.1.30.1
; EXE2BIN.EXE found in Windows XP c:\windows\system32
;
;Assemble
; tasm boot.asm
;
;Link
; tlink boot.obj,boot.exe
;
;Convert to BIN
; exe2bin boot.exe
;
;
;I do not know what compiler/assembler the M$ guy who wrote this used.
;Parts of the code seem like written in C language. I tried to translate
;those codes to C but failed. It must be a C compiler with extreme
;optimization ability to generate codes like this. Especially to declare
;an external label in assembly for a goto in a C function.(ReadSector,jc MemErr)
;Does anyone know how to declare an external label, please tell me.
;
;The machine code generated are exactly the same to the binary extracted
;from CD. Some conditional jumps(jc/je...) TASM generates are rel8, but rel16
;in original binary. I have to overwrite them with exact bytes to match the
;original binary.
;
;
;
;Have fun !!
;
;A bored engineer
;8 July 2004
;

.386
model tiny

INT8VECTOR equ 20h
PRIMARY_DIRECTORY_RECORD equ 09ch

DISK_ADDRESS_PACKET struc
 SizeOfPacket db ?
 Reserved db ?
 NumOfBlock dw ?
 Buffer dd ?
 sBlockNumberL dd ?
 sBlockNumberH dd ?
ends

IOS_DIRECTORY_RECORD struc
 rec_length db ?
 ext_attr_length db ?
 extent dd ?
 extent_bigendian dd ?
 entsize dd ?
 entsize_bigendian dd ?
 date db 7 dup(?)
 flags db ?
 file_unit_size db ?
 interleave db ?
 volumn_sequence_number dd ?
 entname_len db ?
 entname db ?
ends

cdboot segment byte public use16 'CODE'
  assume cs:cdboot, ds:cdboot

  org 0

start:
  cli
  xor ax,ax
  mov ss,ax
  mov sp,7C00h
  sti
  mov ax,cs
  mov ds,ax
  push dx ; save drive number
  call $+3
here:
  pop si
  sub si,offset here

  jz short ipzero ; jump if IP=0
  cmp si,7C00h
  jne short Error1 ; error code 1 if IP != 7c00h
  mov ax,cs
  cmp ax,0
  jne short Error2 ; error code 2 if cs != 0

  db 0EAh ; jmp far to segment 7c0h
  dw offset continue
  dw 7c0h

ipzero:
  mov byte ptr aCannotBoot+24h,'3' ; set error code 3
  nop
  mov ax,cs
  cmp ax,7C0h
  jne short Error3 ; error code 3 if not 7c0h:0

; cs must be 7c0h here
continue:
  mov ax,cs
  mov ds,ax
  mov byte ptr aCannotBoot+24h,'4' ; set error code 4
  nop
  cmp dl,80h
  jb short BootErr3 ; Jump if drive number < 80h,
           ; error 4
  mov byte ptr aCannotBoot+24h,'5' ; set error code 5
  nop
  mov bx,offset MagicMark
  mov ax,[bx]
  cmp ax,0AA55h ; check magic mark
  jne short BootErr3 ; Jump if not equal, error 5
  jmp short SetupLoader
  nop

Error1:
  jmp short BootErr1
  nop

Error2:
  jmp short BootErr2
  nop

Error3:
  jmp short BootErr3
  nop

SetupLoader:
  pop dx
  mov DriveNum,dl
  push offset aBootfix ; param3, file name 'BOOTFIX.BIN'
  push 11 ; param2, file name length
  push 2000h ; param1, memory address to load
  call LoadFile
;-------------------------------
  ;jb NoBootFix ; Jump if failed
  db 0fh,82h
  dw offset NoBootFix - offset Here1
Here1:
;-------------------------------
  pusha
  push ds
  push es
  mov dl,DriveNum

  db 9Ah
  dw 0, 2000h ; call bootfix 2000h:0

  pop es
  pop ds
  popa ; Restore all regs

NoBootFix:
  call ChooseOption ; display option message

  push offset aSetupldr ; param3, file name 'SETUPLDR.BIN'
  push 12 ; param2, file name length
  push 2000h ; param1, memory address to load
  call LoadFile
;-------------------------------
  ;jc short NTLDRErr ; Jump if failed
  db 0fh,82h
  dw offset NTLDRErr - offset Here2
Here2:
;-------------------------------
  mov dl,DriveNum ; dl = drive number
  xor ax,ax
  push 2000h
  push ax
  retf ; go to 2000h:0 SETUPLDR, no return

BootErr1:
  push si
  mov bx,si
  add bx,offset aCannotBoot+24h
  mov byte ptr [bx],'1' ; Set error code 1
  add si,offset aCannotBoot
  jmp short ShowError
  nop
BootErr2:
  push si
  mov bx,si
  add bx,offset aCannotBoot+24h
  mov byte ptr [bx],'2' ; 'Set error code 2
  add si,offset aCannotBoot
  jmp short ShowError
  nop
BootErr3:
  push 0
  mov si,offset aCannotBoot ; error code 0
  jmp short ShowError
  nop
NTLDRErr:
  push 0
  mov si,offset aNoNTLDR ; NTLDR error
  jmp short ShowError
  nop
MemErr:
  push 0
  mov si,offset aMemError ; Memory error
  jmp short ShowError
  nop
ShowError:
  call PrintStr
  pop si
  jmp Restart

;---------------------------------------------------------------------------

PrintStr:
  lodsb ; read [si] to al
  or al,al ; end of string ?
;-------------------------------
  ;jz PrintStr_ret ; Jump if end of string
  db 0fh,84h
  dw offset PrintStr_ret - offset Here3
Here3:
;-------------------------------
  mov ah,0Eh
  mov bx,7
  int 10h ; Video display ah=functn 0Eh, print a character
  jmp short short PrintStr
PrintStr_ret:
  ret

;---------------------------------------------------------------------------

ChooseOption:
  push si
  mov si,offset aBootMessage
  call PrintStr1
  mov cx,80h ;

clearloop:
; clear all keystrokes
  mov ah,1
  int 16h ; check for key stroke
       ; get status, if zf=0 al=char
;-------------------------------
  ;jz short GetKey ; Jump if NO keystroke available
  db 0fh,84h
  dw offset GetKey - offset Here4
Here4:
;-------------------------------

  mov ah,0 ;
  int 16h ; Get keystroke
  loop clearloop ; and discard

GetKey:
  mov ah,0
  int 16h

GetKey1:
  cmp al,'A'
  jl short GetKey ; Jump if < 'A' discard
  cmp al,'z'
  jg short GetKey ; Jump if > 'z' discard
  dec al
  and al,1Fh
  cmp al,NumOfOptions
;-------------------------------
  ;jl short OptionAllowed ; Jump if < NumOfOptions
  db 0fh,8ch
  dw offset OptionAllowed - offset Here5
Here5:
;-------------------------------
  jmp short GetKey

OptionAllowed:
  mov OptionSelected,al
  push ax
  push bx
  add al,'A'
  mov ah,0Eh
  mov bx,7
  int 10h ; print the key out
  pop bx
  pop ax

  mov ah,0
  int 16h ; Get keystroke
  cmp al,0Dh
;-------------------------------
  ;je short coret ; jump if 'enter' pressed
  db 0fh,84h
  dw offset coret - offset Here6
Here6:
;-------------------------------

  push ax
  mov al,8
  mov ah,0Eh
  mov bx,7
  int 10h ; write char al
  pop ax
  jmp short GetKey1
coret:
  pop si
  ret

;---------------------------------------------------------------------------

PrintStr1:
  push ax
  push bx
psloop:
  lodsb
  cmp al,0
;-------------------------------
  ;je short psret
  db 0fh,84h
  dw offset psret - offset Here7
Here7:
;-------------------------------
  mov ah,0Eh
  mov bx,7
  int 10h

  jmp short psloop
psret:
  pop bx
  pop ax
  ret

;---------------------------------------------------------------------------

Restart:
  mov TimeCount[si],24h
  cli
  push es
  xor ax,ax
  mov es,ax

; save old int8 vector

  mov bx,INT8VECTOR
  mov ax,es:[bx]
  mov OldInt8Off[si],ax
  mov ax,es:[bx+2]
  mov OldInt8Seg[si],ax
  mov es:[bx],si
  add word ptr es:[bx],offset int8isr
  mov es:[bx+2],cs
  pop es
  sti ; Enable interrupts

WaitTimeOut:
  cmp TimeCount[si],0
  jne short WaitTimeOut ; continue till timeout

; restore int8 vector
  cli
  push es
  xor ax,ax
  mov es,ax
  mov bx,INT8VECTOR
  mov ax,OldInt8Off[si]
  mov es:[bx],ax
  mov ax,OldInt8Seg[si]
  mov es:[bx+2],ax
  pop es
  sti ; Enable interrupts

; copy entire body to 2000h:0
  push ds
  push es
  mov ax,2000h
  mov es,ax
  mov ax,cs
  mov ds,ax
  xor di,di
  mov cx,800h
  rep movsb
  pop es
  pop ds

  db 0EAh
  dw offset RestartPoint, 2000h ; jmp 2000h:RestartPoint

;-------------------
RestartPoint:
  push es
  mov ax,7c0h
  mov es,ax
  mov bx,0
  mov ax,201h
  mov cx,1
  mov dx,80h
  int 13h ; Disk dl=drive 0 ah=func 02h
       ; read sectors to memory es:bx
       ; al=#,ch=cyl,cl=sectr,dh=head
;-------------------------------
  ;jnc short readok ; Jump if carry=0
  db 0fh,83h
  dw offset readok - offset Here8
Here8:
;-------------------------------
deadloop:
  jmp short deadloop
readok:
  pop es
  mov dl,80h

  db 0EAh
  dw 7C00h, 0 ; jmp far 0:7c00h

;-------- Timer ISR -------------
int8isr:
  pushf
  cli
  cmp cs:TimeCount[si],0
;-------------------------------
  ;je short CountEnd ; Jump if TimeCount == 0
  db 0fh,84h
  dw offset CountEnd - offset Here9
Here9:
;-------------------------------
  dec cs:TimeCount[si] ; decrese by 1
CountEnd:
  popf
  push cs:OldInt8Seg[si]
  push cs:OldInt8Off[si]
  retf ; back to original ISR

aCannotBoot db 'CDBOOT: Cannot boot from CD - Code: 0', 0Dh, 0Ah, 0
aNoNTLDR db 'CDBOOT: Couldn', 27h, 't find NTLDR', 0Dh, 0Ah, 0
aMemError db 'CDBOOT: Memory overflow error', 0Dh, 0Ah, 0

;---------------------------------------------------------------------------
;---------------------------------------------------------------------------

FindEntry proc near
  mov byte ptr SegmentCrossing,0
  nop
  mov cx,BytesToReadL
  cld ; Clear direction
  xor bx,bx ; Zero register
  xor dx,dx ; Zero register
loc_37:
  mov si,DirName
  mov dl,es:[bx]
  cmp dl,0
;-------------------------------
  ;je loc_42 ; Jump if equal
  db 0fh,84h
  dw offset loc_42 - offset Here10
Here10:
;-------------------------------
  mov ax,bx
  add ax,21h
  mov di,ax
  push cx
  xor cx,cx ; Zero register
  mov cl,DirNameLen
  repe cmpsb ; Rep zf=1+cx >0 Cmp [si] to es:[di]
  pop cx
;-------------------------------
  ;jz loc_51 ; Jump if zero
  db 0fh,84h
  dw offset loc_51 - offset Here10_1
Here10_1:
;-------------------------------
loc_39:
  cmp dx,cx
;-------------------------------
  ;jae loc_46 ; Jump if above or =
  db 0fh,83h
  dw offset loc_46 - offset Here10_2
Here10_2:
;-------------------------------
  sub cx,dx
  cmp byte ptr SegmentCrossing,1
;-------------------------------
  ;je loc_43 ; Jump if equal
  db 0fh,84h
  dw offset loc_43 - offset Here10_3
Here10_3:
;-------------------------------
loc_41:
  add dx,bx
  mov bx,dx
  and bx,0Fh
  push cx
  mov cl,4
  shr dx,cl ; Shift w/zeros fill
  pop cx
  mov ax,es
  add ax,dx
  mov es,ax
  jmp short loc_37
loc_42:
  mov dx,1
  jmp short loc_39
loc_43:
  inc cx
  mov byte ptr SegmentCrossing,0
  nop
  jmp short loc_41
loc_45:
  mov byte ptr SegmentCrossing,1
  nop
  jmp short loc_49
  nop
loc_46:
  cmp BytesToReadH,0
;-------------------------------
  ;jne loc_47 ; Jump if not equal
  db 0fh,85h
  dw offset loc_47 - offset Here11
Here11:
;-------------------------------
  stc ; Set carry flag
  retn
loc_47:
  sub BytesToReadH,1
  add bx,dx
  push bx
  push cx
  mov cl,4
  shr bx,cl ; Shift w/zeros fill
  pop cx
  mov ax,es
  add ax,bx
  mov es,ax
  pop bx
  and bx,0Fh
  sub dx,cx
  jz loc_45 ; Jump if zero
  dec dx
loc_49:
  mov ax,0FFFFh
  sub ax,dx
  mov cx,ax
  jmp loc_37
loc_51:
  cmp byte ptr FindDirEntry,1
;-------------------------------
  ;je loc_52 ; Jump if equal
  db 0fh,84h
  dw offset loc_52 - offset Here12
Here12:
;-------------------------------
  test byte ptr es:[bx+ IOS_DIRECTORY_RECORD.flags],2
  jnz short loc_39 ; Jump if not zero
  jmp short loc_53
  nop
loc_52:
  test byte ptr es:[bx+IOS_DIRECTORY_RECORD.flags],2
  jz loc_39 ; Jump if zero
loc_53:
  mov al,DirNameLen
  cmp es:[bx+20h],al
  jne loc_39 ; Jump if not equal
  clc ; Clear carry flag
  retn
FindEntry endp

;---------------------------------------------------------------------------
;---------------------------------------------------------------------------

ReadSectors proc near

PARAMETER_1 = 4 ; bp+4
PARAMETER_2 = 6 ; bp+6
PARAMETER_3 = 8 ; bp+8
PARAMETER_4 = 0Ah ; bp+0Ah
PARAMETER_5 = 0Ch ; bp+0Ch

  push bp
  mov bp,sp
  push bx
  push si
  push dx
  push ax
  mov bx,offset dap
  mov byte ptr [bx+DISK_ADDRESS_PACKET.SizeOfPacket],10h
  mov byte ptr [bx+DISK_ADDRESS_PACKET.Reserved],0
  mov byte ptr [bx+DISK_ADDRESS_PACKET.NumOfBlock+1],0
  mov word ptr [bx+DISK_ADDRESS_PACKET.Buffer],0
  mov word ptr [bx+DISK_ADDRESS_PACKET.sBlockNumberH],0
  mov word ptr [bx+DISK_ADDRESS_PACKET.sBlockNumberH+2],0
  mov ax,[bp+PARAMETER_5]
  mov TotalBlockToReadL,ax
  mov ax,[bp+PARAMETER_4]
  mov TotalBlockToReadH,ax
  mov ax,[bp+PARAMETER_3]
  mov BufferAddr,ax
  mov ax,[bp+PARAMETER_2]
  mov BlockNumberL,ax
  mov ax,[bp+PARAMETER_1]
  mov BlockNumberH,ax
loc_56:
;-------------------------------
  ;cmp word ptr TotalBlockToReadH,0
  db 81h,3eh
  dw offset TotalBlockToReadH
  dw 0
;-------------------------------
;-------------------------------
  ;jnz loc_58 ; Jump if not zero
  db 0fh,85h
  dw offset loc_58 - offset Here13
Here13:
;-------------------------------
;-------------------------------
  ;cmp TotalBlockToReadL,20h
  db 81h,3eh
  dw offset TotalBlockToReadL
  dw 20h
;-------------------------------
;-------------------------------
  ;jg loc_58 ; Jump if >
  db 0fh,8fh
  dw offset loc_58 - offset Here13_1
Here13_1:
;-------------------------------
  mov byte ptr fMoreBlocks,0
  nop
  mov ax,TotalBlockToReadL
  mov BlockToRead,al
  jmp short loc_59
  nop
loc_58:
  mov byte ptr fMoreBlocks,1
  nop
  mov byte ptr BlockToRead,20h ; ' '
  nop
loc_59:
  mov al,BlockToRead
  mov byte ptr [bx+DISK_ADDRESS_PACKET.NumOfBlock],al
  mov ax,BufferAddr
  mov word ptr [bx+DISK_ADDRESS_PACKET.Buffer+2],ax
  mov ax,BlockNumberL
  mov word ptr [bx+DISK_ADDRESS_PACKET.sBlockNumberL],ax
  mov ax,BlockNumberH
  mov word ptr [bx+DISK_ADDRESS_PACKET.sBlockNumberL+2],ax
  mov si,offset dap
  mov ah,42h ; 'B'
  mov dl,DriveNum
  int 13h ; ??int non-standard interrupt
  cmp byte ptr fMoreBlocks,1
;-------------------------------
  ;jne loc_61 ; Jump if not equal
  db 0fh,85h
  dw offset loc_61 - offset Here14
Here14:
;-------------------------------
  sub TotalBlockToReadL,20h
  sbb TotalBlockToReadH,0
  add BufferAddr,1000h
  jc MemErr
;-------------------------------
  ;add BlockNumberL,20h
  db 81h,06h
  dw offset BlockNumberL
  dw 20h
;-------------------------------
;-------------------------------
  ;adc BlockNumberH,0
  db 81h,16h
  dw offset BlockNumberH
  dw 0
;-------------------------------
  jmp short loc_56
loc_61:
  pop ax
  pop dx
  pop si
  pop bx
  mov sp,bp
  pop bp
  retn
ReadSectors endp

;---------------------------------------------------------------------------
;---------------------------------------------------------------------------

ReadToMemory proc near

PARAMETER_1 = 4 ; bp+4

  push bp
  mov bp,sp
  push cx
  push bx
  push ax
  mov cl,0Bh
  mov bx,BytesToReadH
  mov ax,BytesToReadL
  shrd ax,bx,cl ; Double shift right
  shr bx,cl ; Shift w/zeros fill
  test BytesToReadL,7FFh
  jz short loc_62 ; Jump if zero
  add ax,1
  adc bx,0
loc_62:
  push ax ; PARAMETER_5
  push bx ; PARAMETER_4
  push word ptr [bp+PARAMETER_1] ; PARAMETER_3
  push StartBlockL ; PARAMETER_2
  push StartBlockH ; PARAMETER_1
  call ReadSectors
  add sp,0Ah
  pop ax
  pop bx
  pop cx
  mov sp,bp
  pop bp
  retn
ReadToMemory endp

;---------------------------------------------------------------------------
;---------------------------------------------------------------------------

GetSectorFromRecord proc near
  push ax
  mov ax,word ptr es:[bx+IOS_DIRECTORY_RECORD.extent]
  mov StartBlockL,ax
  mov ax,word ptr es:[bx+IOS_DIRECTORY_RECORD.extent+2]
  mov StartBlockH,ax
  mov ax,word ptr es:[bx+IOS_DIRECTORY_RECORD.entsize]
  mov BytesToReadL,ax
  mov ax,word ptr es:[bx+IOS_DIRECTORY_RECORD.entsize+2]
  mov BytesToReadH,ax
  pop ax
  retn
GetSectorFromRecord endp

;---------------------------------------------------------------------------
;---------------------------------------------------------------------------

;LoadFile(BYTE* addr, BYTE namelen, char* filename);
LoadFile proc near

PARAMETER_1 = 4 ; bp+4
PARAMETER_2 = 6 ; bp+6
PARAMETER_3 = 8 ; bp+8

  push bp
  mov bp,sp
  push 1 ; PARAMETER_5
  push 0 ; PARAMETER_4
  push 1000h ; PARAMETER_3
  push 10h ; PARAMETER_2
  push 0 ; PARAMETER_1
  call ReadSectors
  add sp,0Ah
  mov ax,1000h
  mov es,ax

  mov ax,word ptr es:[PRIMARY_DIRECTORY_RECORD+IOS_DIRECTORY_RECORD.extent]
  mov StartBlockL,ax
  mov ax,word ptr es:[PRIMARY_DIRECTORY_RECORD+IOS_DIRECTORY_RECORD.extent+2]
  mov StartBlockH,ax
  mov ax,word ptr es:[PRIMARY_DIRECTORY_RECORD+IOS_DIRECTORY_RECORD.entsize]
  mov BytesToReadL,ax
  mov ax,word ptr es:[PRIMARY_DIRECTORY_RECORD+IOS_DIRECTORY_RECORD.entsize+2]
  mov BytesToReadH,ax
  push 1000h ; PARAMETER_1
  call ReadToMemory
  add sp,2
  mov ax,0
  mov al,OptionSelected
  mul DirNameLength ; ax = data * al
  add ax,offset OptionDirs
  mov DirName,ax
  mov byte ptr DirNameLen,4
  nop
  mov byte ptr FindDirEntry,1
  nop
  call FindEntry
  jc short loc_64 ; Jump if carry Set
  call GetSectorFromRecord
  push 1000h ; PARAMETER_1
  call ReadToMemory
  add sp,2
  mov ax,1000h
  mov es,ax
  mov ax,[bp+PARAMETER_3]
  mov DirName,ax
  mov al,[bp+PARAMETER_2]
  mov DirNameLen,al
  mov byte ptr FindDirEntry,0
  nop
  call FindEntry
  jc short loc_64 ; Jump if carry Set
  call GetSectorFromRecord
  push word ptr [bp+PARAMETER_1] ; PARAMETER_1
  call ReadToMemory
  add sp,2
loc_64:
  pop bp
  retn
LoadFile endp

OldInt8Off dw 0 ; Data table (indexed access)
OldInt8Seg dw 0 ; Data table (indexed access)
TimeCount dw 24h ; Data table (indexed access)
dap dw 8 dup (0)
fMoreBlocks db 0
aSetupldr db 'SETUPLDR.BIN'
aBootfix db 'BOOTFIX.BIN'
aI386 db 'I386'
DriveNum db 0
StartBlockL dw 0
StartBlockH dw 0
BytesToReadL dw 0
BytesToReadH dw 0
TotalBlockToReadL dw 0
TotalBlockToReadH dw 0
BufferAddr dw 0
BlockNumberL dw 0
BlockNumberH dw 0
BlockToRead db 0
DirName dw 0
DirNameLen db 0
FindDirEntry db 0
SegmentCrossing db 0
aBootMessage db 0Dh, 0Ah
     db '[A] Windows XP Home Edition - Traditional Chinese', 0Dh, 0Ah
     db '[B] Windows XP Professional - Traditional Chinese', 0Dh, 0Ah
     db 0Dh, 0Ah, 'Choose an option and press [Enter]: ', 0

OptionSelected db 0
NumOfOptions db 2
DirNameLength db 4
OptionDirs db 'XHTWXPTW'

     org 7feh
MagicMark dw 0AA55h

cdboot ends

end start
;=================================================================================
;=================================================================================
;=================================================================================



Relevant Pages