|
Building your compilation |
|
In this section I will explain the making of the necessary files. You should be able to suffice without downloading anything off this site if you are limited to Google cache, for example.
BOOTZWZ.BIN
This file is the heart of the improvements in ZWZ PE. Compiles only with nbasm32.exe.
BOOTZWZ.ASM
.model tiny
.386
.code
;
; ()) ZWZ, 2006. Use this according to the GNU-GPL.
; The bootsect.dat is (c) Microsoft, get it from a Windows XP CD
; with ISO-Buster and cut the first 4 bytes, then truncate to
; 1182 bytes in length.
; The isol2inc.bin is from the SYSLINUX 3.31 package, derived
; from isolinux-debug.bin. Copyright (C) 1994-2003 H. Peter Anvin
;
; A bootloader for BartPE
;
; It detects the amount of RAM and chooses a bootup strategy.
; Less than 64 MB - pauses and waits for user input.
; 64 to 290 MB - loads BartPE from the CD.
; More, typically 384 or 512 - loads BartPE into RAMDISK, which
; allows a user to eject the CD during the session.
;
; The ZWZ bootloader accepts the following keyboard input.
; If less than 64 MB RAM, the computer will pause and wait.
; If more, the user has to press the key several times upon
; the POST finish, much like F8 in Windows.
; The keys are:
; Q - force RAMDISK. Only use this if you have 256 MB RAM,
; lower configurations will fail.
; C - CD boot. Override the RAMDISK if there's enough memory
; or force it if there's not enough memory.
; P - Partition Expert. This will load ISOLINUX, which is now
; (on my machine, anyway) associated with it. This functionality
; requires that the machine's BIOS can load two CD sectors!
; If it freezes or shows stange behavior after pressing P,
; you may try to upgrade BIOS, but I don't trust them to fix it.
; N - next device in the bootup chain. You may use this if you don't
; want to eject the CD/DVD with BartPE.
; R - reboot the system. You can use this if you forgot to press
; DEL or F2 to enter Setup. This function is so powerful that
; it can shoot Windows XP/2003 if run as a COM file.
; space or any other key (only below 64 MB RAM) - try to boot up
; from CD again. You can use it to save time rebooting after
; you have inserted another bootable CD.
;The code begins...
uplnanula:
jmp zacatek
nop ; The jump has 3 bytes, but the bootzwz.dat has 4 bytes missing.
incbin bootzwz.dat
zacatek:
pusha
mov ax,cs
shl eax,16
mov esi,eax
mov edi,eax ; these should ensure that we operate on the code segment
mov eax,'cs '
call sub_texteax
mov ax,cs
call sub_psanieax
mov eax,'ds '
call sub_texteax
mov ax,ds
call sub_psanieax
mov eax,'es '
call sub_texteax
mov ax,es
call sub_psanieax
mov eax,'ZWZ ' ; starts
call sub_texteax
mov eax,0D0A0D0Ah
call sub_texteax
xor cx,cx
cli
;********************* SPECIAL ATTENTION REQUIRED! *************************
db 0EAh,0F3h,04h,0C0h,07h ; Patch this to point to the three NOPs below visible in a hex editor!
; EA is far jump, 04 (example) is the high byte of the "nalez" address, F3 (right after EA) is the low one.
; By this, we trash any relative offsets and have a sure 07C0:0000!
nalez:
nop
nop
nop
;*****************************************************************************
sti
mov bx,cx
mov eax, 08EC033FAh ; the 4 bytes that belonged to the start of the original bootsector
mov di,0
mov cs:[di],eax
mov eax,'ofs.'
call sub_texteax
mov ax,bx
call sub_psanieax
mov eax,'sig.' ; Reads the signature 55AA at the end of the file. If not AA55, check the iso.bat for the sector count option and fix it, a 8192 B file needs 16 sectors.
call sub_texteax
mov si,offset zakoncem
sub si,258
mov ax,cs:[si]
call sub_psanieax
cmp ax,0AA55h
je zastiznost
mov eax,'pau.'
call sub_texteax
mov ax,47829 ; makes a BAD5 text
call sub_psanieax
xor ax,ax ; prevents some continuing keyboard function to occur
int 16h
zastiznost:
mov eax,0E801h ; prepare for "total memory" BIOS service
int 15h
xor eax,eax
mov eax,edx
shr eax,4
add eax,18 ; Extended memory. It should be 16, but the output was somehow 2 MB off, so I adjusted it.
push eax
mov eax,'ram.' ; Reports total system RAM in MB, but HEX, of course.
call sub_texteax
pop eax
push eax
call sub_psanieax
pop eax
cmp eax,60 ; min RAM, lower this to 50 if the CD won't start on a computer with exactly 64 MB RAM.
jbe mocmalo
cmp eax,290 ; from CD - enough memory threshold. 290 MB seems OK for a 174 MB nested ISO.
jbe mensi ; If you manage to shrink the ISO more, you may edit this value to work on 256 MB computers. Use 250 then to avoid memory holes and rounding errors.
ramdisk:
mov ah,1
int 16h
jz nicnezm
cmp al,'n'
je nextdev
cmp al,'p'
je partmgr
cmp al,'c'
je mensi
cmp al,'r'
je reset
nicnezm:
mov eax,'TRAB' ; reversed BART, the first 4 bytes
xor bx,bx
add bx,047Eh ; start of the SETUPLDR filename - study this in the uncut bootsect.bin, the bootzwz.dat lacks the first 4 bytes!
mov di,bx
mov cs:[di],eax
mov eax,'RDLP' ; PLDR, the second 4 bytes
xor bx,bx
add bx,0482h ; the rest, 4 bytes later
mov di,bx
mov cs:[di],eax
mov eax,'mtd.'
call sub_texteax
mov eax,'rdsk'
call sub_texteax
popa
jmp offset uplnanula
int 19h
int 20h
partmgr:
mov eax,'mtd.'
call sub_texteax
mov eax,'pmgr'
call sub_texteax
;Copy oneself 4096 bytes later.
xor ax,ax
mov si,ax ; source is this start
add ax,4096
mov di,ax
xor bx,bx
ks_opak:
mov eax,cs:[si]
mov cs:[di],eax
add si,4
add di,4
inc bx
cmp bx,512 ; This copies 4 bytes with each loop.
je ks_konec
jmp ks_opak
ks_konec:
db 0E9h,0h,10h ; jump to the copied code of oneself
sem:
xor ax,ax
mov di,ax ; destination is the old start
add ax,2048
mov si,ax ; source is the isolinux overlay
xor bx,bx
ki_opak:
mov eax,cs:[si]
mov cs:[di],eax
add si,4
add di,4
inc bx
cmp bx,512 ; This copies 4 bytes with each loop, 2048 bytes (one CD sector) total.
je ki_konec
jmp ki_opak
ki_konec:
popa ; restores (also) DL, which holds the boot drive number - important
cli ; this is the same what is at the start of isolinux.
db 0EAh,040h,07Ch,0h,0h ; this is after the cli in isolinux, it's an absolute jump, which we now take advantage of
int 19h ; reinitiate boot; but if the jump failed, it's a lost battle now anyway
mensi:
mov eax,'mtd.'
call sub_texteax
mov eax,'cd '
call sub_texteax
mov ah,1
int 16h
jz nicnezm2
cmp al,'n'
je nextdev
cmp al,'q'
je ramdisk
cmp al,'p'
je partmgr
cmp al,'r'
je reset
nicnezm2:
popa
jmp offset uplnanula
int 19h
mocmalo:
mov eax,'mtd.'
call sub_texteax
mov eax,'fail'
call sub_texteax
xor ax,ax ; al=0 means Wait for a keypress
int 16h ; press a key
cmp al,'n'
je nextdev
cmp al,'p'
je partmgr
cmp al,'c'
je mensi
cmp al,'r'
je reset
popa
int 19h ; boot again
nextdev:
int 18h
int 19h
reset:
mov al,0FEh
out 64h,al ; Resets the system.
int 19h
sub_psanieax:
pusha
mov ecx,eax
rol ecx,8
xor eax,eax
xor edx,edx
mov ah, 02h
mov dl,cl
; call vypishex ; Uncomment to display the extension of the registry too.
rol ecx,8
mov ah, 02h
mov dl,cl
; call vypishex ; Uncomment to display the extension of the registry too.
rol ecx,8
mov ah, 02h
mov dl,cl
call vypishex
rol ecx,8
mov ah, 02h
mov dl,cl
call vypishex
mov ah, 0Eh
mov al,0Dh
int 10h
mov ah, 0Eh
mov al,0Ah
int 10h
popa
ret
vypishex:
pusha ; This converts an individual byte into a hex text and writes it out.
mov al,dl
and al,0Fh
mov ah,dl
shr ah,4
add al,48
add ah,48
cmp al,58
jl aldobry
add al,7
aldobry:
cmp ah,58
jl ahdobry
add ah,7
ahdobry:
mov bx,ax
; Muze se vypisovat
xor eax,eax
mov ah, 0Eh
mov al,bh
int 10h
mov ah, 0Eh
mov al,bl
int 10h
; Koncime s hex vypisem
popa
ret
sub_texteax:
pusha ; Writes out short 4-character messages that are not dependent on any constant that would be linked absolutely. Use mov eax,'msg4' to prepare the message.
mov ecx,eax
rol ecx,8
xor eax,eax
xor edx,edx
mov ah, 0Eh
mov al,cl
int 10h
rol ecx,8
mov ah, 0Eh
mov al,cl
int 10h
rol ecx,8
mov ah, 0Eh
mov al,cl
int 10h
rol ecx,8
mov ah, 0Eh
mov al,cl
int 10h
popa
ret
dup 125,0
db 0Dh,0Ah, 'ZWZ Bootsector for BartPE.',0Dh,0Ah
db 055h,0AAh ; boot signature
zakoncem:
incbin isol2inc.bin
;******************* DO NOT UNCOMMENT ANY OF THESE *********************
; The isolinux-debug.bin is modified to read the rest of itself not from 2K,
; but from 4K. (This occupies the first 2KB.) It was necessary to add another
; inc instruction here:
; mov [ImageSectors],ax ; boot file sectors
; mov eax,[bi_file] ; Address of code to load
; inc eax ; Don't reload bootstrap code
; %ifdef DEBUG_MESSAGES
; mov si,offset_msg
; call writemsg
; call writehex8
; call crlf
; %endif
; I wrote the "inc eax" over the "mov si,offset_msg" and NOP'd the rest
; of the message calls.
; I also had to kill the CRC routine.
; .done: mov ax,ds
; mov es,ax
; cmp [bi_csum],edi
; je integrity_ok
; mov si,checkerr_msg
; In this snippet, replace the "je" opcode 74h with EBh = jmp.
; -ZWZ-
.end
As you can see, this file uses two binaries. To obtain bootzwz.dat, run ISOBuster, open the Windows CD (any XP will suffice) and save the BootImage (2 KB) onto disk. The file is also supplied by PE Builder as bootsect.bin. Then use a hex editor (edit /64 is enough) and cut it like this:
Delete the first 4 bytes. Save and verify that the file is now 2044 bytes long. Then delete from the end and truncate the file to 1182 bytes in length.
Next is the isol2inc.bin. Download the SYSLINUX package and extract isolinux-debug.bin from it. The non-debug version is unusable in this project! Follow the remarks at the end of the ASM code. I used Sourcer as a disassembler for this purpose. If you have access to Linux with all the includes that ISOLINUX.ASM requires, you can just double the inc eax instruction and compile it and that should be OK.
Now run nbasm32 bootzwz.asm and it should produce bootzwz.com. When renaming to bootzwz.bin, pay attention to making it lowercase or it won't work right!
Put bootzwz.bin into the root BartPE CD directory and also into a new Include directory, which has to be configured in PE Builder. This is to assure that the file will be added when you rebuild the CD with PE Builder.
BARTPLDR
We need two separate LDRs, one that will handle low-memory systems booting from CD and another that will copy the CD into RAM first so that you can use the optical drive for other purposes later. (That requires more RAM, however.)
Here is how you can prepare BARTPLDR.BIN. The goal is to make this file use a different configuration than SETUPLDR.BIN. We cannot just add [CD_root]\winnt.sif, because SETUPLDR would find it also. So it's necessary to change the WINNT.SIF in the binary file to something else.
Copy i386\SETUPLDR.BIN to BARTPLDR.BIN (same directory). Load it into a hex editor, edit /64 is almost enough, but be cautious and make sure you know how to handle the old DOS editor.
Search for WINNT.SIF in bartpldr and after toggling Insert to Overwrite mode, overwrite W to V and T to U, keep the letter case the same, i.e. don't replace t with U and so on. The idea behind this is to keep the CRC the same. If you made up an arbitrary name instead of WINNT.SIF, the file would report NTLDR corrupted (regardless the fact that it's not identical to NTLDR and is originally SETUPLDR...)
If you used edit, save it and exit and then load the same file again, now with the row width of 65 characters (as opposed to 64). EDIT won't find WINNT occurences that broke on the end of the line, so fix the rest of WINNT's to VINNU. Save and place this file into [include_dir]\i386 to keep it after the builder erases the CD directory during any future rebuild.
[cd_root+include]\VINNU.SIF
[SetupData]
BootDevice = "ramdisk(0)"
BootPath = "\I386\SYSTEM32\"
OsLoadOptions = "/noguiboot /fastdetect /minint /rdexportascd /rdpath=ramdisk.iso"
Just copy-paste-save this, there's nothing special on this one except that you may want to customize the ramdisk.iso filename into something else. But I do not recommend changing anything as it's likely that it will break the compatibility with some other file.
[cd_root+include]\RAMDISK.000
Yes, this file has to be created empty. It has to have the same filename length and sort alphabetically the same as the future RAMDISK.ISO and I only chose to use a different extension to see any build problems easily.
Self-reference patch
Now this is a tricky goodie! This will patch the file RAMDISK.000 in the finished ISO in a way that it will start at sector 0 and have the same length as the ISO, effectively allowing the Windows loader and the user to directly grab the entire CD without any CD-cloning tool. In a previous incarnation, a separately-built ISO was added to the compilation, but this was very inefficient and the PE could not fit on an 8-cm CD. So this solves the problem. One interesting point is that the self-reference continues infinitely, allowing the user to burn a new ZWZ PE CD from within the running environment using CDRECORD. Just supply X:\RAMDISK.ISO as the source CD image.
A patcher done in Visual Basic 6 follows. Add it to a blank project and set it as the startup module.
islfForm.frm
VERSION 5.00
Begin VB.Form Form1
BorderStyle = 1 'Fixed Single
Caption = "ZWZ PE ISO Patcher"
ClientHeight = 4545
ClientLeft = 45
ClientTop = 360
ClientWidth = 4680
Icon = "islfForm.frx":0000
LinkTopic = "Form1"
MaxButton = 0 'False
ScaleHeight = 4545
ScaleWidth = 4680
StartUpPosition = 2 'CenterScreen
Begin VB.TextBox LogText
Height = 615
Left = 120
MultiLine = -1 'True
ScrollBars = 2 'Vertical
TabIndex = 8
Top = 3840
Width = 4455
End
Begin VB.CommandButton Command1
Caption = "Proceed"
Height = 375
Left = 1200
TabIndex = 7
Top = 3360
Width = 2175
End
Begin VB.TextBox Text3
Height = 285
Left = 120
TabIndex = 6
Text = "RAM_DISK.ISO"
Top = 2640
Width = 4455
End
Begin VB.TextBox Text2
Height = 285
Left = 120
TabIndex = 4
Text = "_SELFREF.000"
Top = 2040
Width = 4455
End
Begin VB.TextBox Text1
Height = 285
Left = 120
TabIndex = 2
Text = "zwzpe.iso"
Top = 1440
Width = 4455
End
Begin VB.Label Label4
Caption = "Rename to (you may not extend it's original name length)"
Height = 255
Left = 120
TabIndex = 5
Top = 2400
Width = 4455
End
Begin VB.Label Label3
Caption = "File to link (without any directory)"
Height = 255
Left = 120
TabIndex = 3
Top = 1800
Width = 4095
End
Begin VB.Label Label2
Caption = "ISO to patch:"
Height = 255
Left = 120
TabIndex = 1
Top = 1200
Width = 2415
End
Begin VB.Label Label1
Caption = $"islfForm.frx":030A
Height = 855
Left = 120
TabIndex = 0
Top = 120
Width = 4455
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False
Dim logname As String
Private Type fileheader
'Total type size should be 32 bytes.
reclen As Byte
rzero As Byte
offset As Long ' to be changed to 0
offsetinv As Long
length As Long ' use the ISO length
lengthinv As Long 'convert Endians
dateattr As String * 7
bitflags As Byte '0 - normal, 1 - hidden, other - invalid!!
rzero2 As Integer
volindex As Long '=16777217 = 01 00 00 01
' Name length (byte) follows, but is handled separately.
End Type
Private Sub Command1_Click()
Dim tofind As String
Dim tfx As String
Dim tfoffset As Long
Dim header As fileheader
Dim i As Long
Dim polohahlavy As Long
Dim pdelkanazvu As Long
Dim delkanazvu As Byte
Dim vlozimnazev As String
tofind = Text2
tfx = tofind
On Error GoTo trap
Open Text1 For Input As #1
Close #1
Open Text1 For Binary As #1
For i = 1 To 256000
Get #1, i, tfx
If tfx = tofind Then Exit For
Next i
If tfx <> tofind Then DebugPrint "Not found!": Reset: Exit Sub
polohahlavy = i - 33
pdelkanazvu = i - 1
Get #1, polohahlavy, header
Get #1, pdelkanazvu, delkanazvu
'Header contents verification
If header.length <> invertlong(header.lengthinv) Then DebugPrint "Pair file lengths do not match!": Reset: Exit Sub
If header.offset <> invertlong(header.offsetinv) Then DebugPrint "Pair file offsets do not match!": Reset: Exit Sub
If header.length <> 0 Then DebugPrint "Warning: File length is not zero - aborting!": Reset: Exit Sub
If Len(Text3) > delkanazvu Then DebugPrint "Cannot use a longer filename - aborting!": Reset: Exit Sub
If Abs(header.bitflags) > 1 Then DebugPrint "It is not a file!": Reset: Exit Sub
If UCase(Text3) <> Text3 Then DebugPrint "Must use UPPERCASE for filenames!": Reset: Exit Sub
'Apply changes
header.length = LOF(1)
header.lengthinv = invertlong(header.length)
header.offset = 0
header.offsetinv = invertlong(header.offset)
Put #1, polohahlavy, header
vlozimnazev = Text3
If Len(vlozimnazev) < delkanazvu Then
vlozimnazev = Left$(vlozimnazev + String$(255, 0), delkanazvu)
End If
Put #1, i, vlozimnazev
delkanazvu = Len(Text3)
Put #1, pdelkanazvu, delkanazvu
Close #1
DebugPrint "OK"
If logname <> "" Then End
Exit Sub
trap:
DebugPrint Err.Description: Reset
Exit Sub
Resume Next
End Sub
Private Sub DebugPrint(msg As String)
If logname <> "" Then
Open logname For Append As #2
Print #2, msg + vbCrLf
Close #2
End If
LogText = LogText + msg + vbCrLf
End Sub
Private Function invertlong(inlong As Long) As Long
Dim ind As Double
Dim ind2 As Double
ind = inlong
If ind < 0 Then ind = ind + 2 ^ 32
ind2 = ind
Dim byte1 As Double
Dim byte2 As Double
Dim byte3 As Double
Dim byte4 As Double
While ind2 > 25600000
ind2 = ind2 - 25600000
Wend
byte1 = ind2 Mod 256
byte2 = Int(ind / 256) Mod 256
byte3 = Int(ind / 65536) Mod 256
byte4 = Int(ind / (256 ^ 3))
invertlong = byte1 * 256 ^ 3 + byte2 * 65536 + byte3 * 256 + byte4
End Function
Private Sub Form_Load()
Dim isoname As String
Dim fname As String
Dim newname As String
a = InStr(1, Command$, "/l=", vbTextCompare)
If a > 0 Then
For i = a + 3 To Len(Command$)
m$ = Mid$(Command$, i, 1)
If m$ = " " Or m$ = "/" Then Exit For
logname = logname + m$
Next i
Text1 = "": Text3 = ""
End If
a = InStr(1, Command$, "/i=", vbTextCompare)
If a > 0 Then
For i = a + 3 To Len(Command$)
m$ = Mid$(Command$, i, 1)
If m$ = " " Or m$ = "/" Then Exit For
isoname = isoname + m$
Next i
Text1 = "": Text3 = ""
If logname <> "" Then
Open logname For Output As #1
Close #1
End If
End If
a = InStr(1, Command$, "/f=", vbTextCompare)
If a > 0 Then
For i = a + 3 To Len(Command$)
m$ = Mid$(Command$, i, 1)
If m$ = " " Or m$ = "/" Then Exit For
fname = fname + m$
Next i
Text1 = "": Text3 = ""
End If
a = InStr(1, Command$, "/n=", vbTextCompare)
If a > 0 Then
For i = a + 3 To Len(Command$)
m$ = Mid$(Command$, i, 1)
If m$ = " " Or m$ = "/" Then Exit For
newname = newname + m$
Next i
Text1 = "": Text3 = ""
End If
If isoname <> "" Then Text1 = isoname
If fname <> "" Then Text2 = fname
If newname <> "" Then Text3 = newname
If logname <> "" Then
Command1_Click
End
End If
End Sub
The code is nothing spiffy, somebody should rewrite it in C or Python or something. Compile it into ISOSELF.EXE or fix the batch file below to reference it correctly.
I build the ISO using a batch file placed in the pebuilder directory. Feel free to customize the CD directory and/or the output ISO name, but I suggest that you keep the default BartPE as the CD directory to avoid unnecessary typos and so on.
[pe_builder]\iso.bat
@echo off
mkisofs.exe -iso-level 4 -force-uppercase -volid "ZWZPE" -b bootzwz.bin -no-emul-boot -boot-load-size 8 -hide boot.catalog -o .\zwzpe.iso "bartpe"
isoself /l=isoself.log /i=zwzpe.iso /f=RAMDISK.000 /n=RAMDISK.ISO
type isoself.log
echo Finished.
This batch file contains some very important parameters for MKISOFS (you got this with PE Builder), such as the option to load the first 8 HDD-style sectors of bootzwz.bin. The "bartpe" string is the CD directory, you may want to change this if you decided to use another name. VolID is actually the CD's label visible in My computer, for example.
|