Pimp my PXE-boot screen

It was “a few” years ago since I did use a HEX-editor… but with a little time over last night I did a small hack to the PXE-boot files.

Why I did it…
1. I wanted to test if I still know how to use a HEX-editor
2. I don’t like the original text.

Do I need to mention: This is NOT supported by anyone and if you break any deployment-solution… don’t blame me.

First, make a backup (*doh*).

The files you are looking for are located on your PXE-point, X:\RemoteInstall\SMSBoot\x86 (and \x64)

I use XVI32 v2.51 www.chmaas.handshake.de/delphi/freeware/xvi32/xvi32.htm to make the changes in the binary files.

Open up PXEBOOT.COM in XVI and search for “Press F12″ (case sensitive).
Then replace the text. If you have a shorter text, fill it out with blank spaces. If you have a longer text… tough luck. :-\

"Press F12 for network service boot"
"Press F12 for snowland deployment "
 

The original text:
hexedit-press-f12

And the changed text:
hexedit-press-f12-snowland

Save and test to PXE-boot a machine.
(If you can’t see the new text, you probably need to copy the changed file to both the x86 and the x64 directory)

That wasn’t to hard, was it?
If it was… don’t bother to try the WDSNBP.COM-file. :-P

The largest textblock that a user can see is the following text, this is what we want to change / pimp.

The details below show the information relating to the PXE boot request for
This computer. Please provide these details to your Windows Deployment Services
Administrator so that this request can be approved.
         1         2         3         4         5         6         7         8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
 

The easy way is just to change the text to something else, but why not use some ascii-art?

If you want to use ascii-art, the maximum width is 51 chars (the width of the third line) and 3 lines high since you need to replace and not add/delete anything.
Of course the first and second line can be a bit longer.

So… start up some texteditor with monospace font (notepad will do) and create some ascii-art.

   __  _  _  __  _    _ _    __  _  _ __
  (__  |\ | |  | |    | |   |__| |\ | |  \  Deployment Services is loading…
  .__) | \| |__|  \/\/  |__ |  | | \| |__/
         1         2         3         4         5         6         7         8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
 

Now that we have some ascii-art with loads of blank spaces we need to replace the right number of spaces with something that we can see.

###__##_##_##__##_####_#_####__##_##_#__###################################
##(__##|\#|#|##|#|####|#|###|__|#|\#|#|##\##Deployment#Services#is#loading…##
##.__)#|#\|#|__|##\/\/##|__#|##|#|#\|#|__/#########
         1         2         3         4         5         6         7         8
12345678901234567890123456789012345678901234567890123456789012345678901234567890
 

The number of # should be exactly the same as the original text.

Start up XVI32 and load WDSNBP.COM, search for “The details below”… now comes the boring/tricky part. Replace the original text with your new ascii-art.

I did a small “translation”-file where I have the orignal row and the new row next to each other, like this for the second row.

This computer. Please provide these defails to your Windows Deployment Services
##(__##|\#|#|##|#|####|#|###|__|#|\#|#|##\##Deployment#Services#is#loading…##
 

Now you can see that the text “computer.” sould be replaced to ” |\ | | “. Yes, it will be hard to read and easy to do wrong.

There is a search/replace option in XVI32, if you use it remember: replace with the exact same number of characters.

Save, PXE-boot and enjoy… :-)

The original:
f12-orginal

The final result:
f12-snowland

The MD5-hash of the WDSNBP.COM and PXEBOOT.COM-files in both x86 and x64 directories is the same so you only have to make the changes in one of them.
wdsnbp-md5-hash


PXE Filter (v4.1.501)

Got a comment on another post from Thomas who was asking for a version of the PXE Filter that works.

I have used this one on a few installations.

You need to configure:
sProviderServer if you have the PXE point on another server
sSiteCode for your site
sCollection to the collection which you advertise your deployment task sequence

What the PXEFilter does is (in general):

  • Check if the machine exist, and if not adds it to the SCCM.
  • Checks if the machine is member of a specific collection (sCollection) and if not adds it to the collection (Direct member)
  • Waits until the machine can see the advertisment (A maximum of 30 seconds)

PXEFilter.vbs v4.1.501 (Download VBS)

' //***************************************************************************
' // ***** Script Header *****
' //
' // Solution:  Microsoft Deployment Toolkit
' // File:      PXEFilter.vbs
' //
' // Purpose:   Decide what needs to be done for each PXE requests received
' //            by WDS.  If not present in the ConfigMgr database, add it.
' //
' // Usage:     (loaded automatically by Microsoft.BDD.PXEFilter DLL)
' //
' // Microsoft Solution Version:  4.1.501
' // Microsoft Script Version:    4.1.501
' // Customer Build Version:      1.0.0
' // Customer Script Version:     1.0.0
' //
' // Microsoft History:
' // 4.0.501 MTN  02/19/2008  Added header, additional logging, advertisement
' //                          verification for every request.
' //
' // Customer History:
' //
' // ***** End Header *****
' //***************************************************************************

'//----------------------------------------------------------------------------
'//
'//  Set global variables, used by the ZTIProcess function below.  (Note that
'//  any changes to this script will require restarting the WDS service, since
'//  the script is only loaded once and kept in memory as long as the service
'//  is running.)
'//
'//  If ConfigMgr is running on the same server as WDS, the sProviderServer
'//  value can be left blank and the sUsername and sPassword values must be
'//  blank.
'//----------------------------------------------------------------------------

Option Explicit

Dim sProviderServer
Dim sSiteCode
Dim sNamespace
Dim sUsername
Dim sPassword
Dim sCollection

sProviderServer = ""
sSiteCode = "ABC"
sNamespace = "root\sms\site_" & sSiteCode
sUsername = ""
sPassword = ""
sCollection = "ABC00004"   ' This must be a collection ID, not a collection name

'//----------------------------------------------------------------------------
'//  Main routine
'//----------------------------------------------------------------------------

ZTIProcess

Function ZTIProcess

        Dim sMacAddress
        Dim sIPAddress
        Dim sUUID
        Dim sNetBiosName
        Dim oLocator
        Dim oSMS
        Dim sQuery
        Dim bFound
        Dim iResourceID
        Dim re
        Dim oSite
        Dim oParams
        Dim oResult
        Dim oLastError
        Dim oClients
        Dim oClient
        Dim oCollection
        Dim oNewRule
        Dim oAdvertisement
        Dim bTrying
        Dim sAdvert
        Dim i

        ' Initialization

        sMacAddress = PXE.MacAddress
        sIPAddress = PXE.IPAddress
        sUUID = PXE.UUID

        Set re = New RegExp
        re.Pattern = ":"
        re.Global = true
        sNetBiosName = "MAC" & re.Replace(sMacAddress, "")

        ' Clear invalid UUID values

        If sUUID = "00000000-0000-0000-0000-000000000000" or sUUID = "FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF" then
                sUUID = ""
        End if

        ' Log details

        PXE.LogTrace "Processing request from MAC address = " & sMacAddress & ", IP address = " & sIPAddress & ", UUID = " & sUUID

        '//----------------------------------------------------------------------------
        '//  Filter requests.  This may need to be customized if you only want some
        '//  network segments to be supported.
        '//----------------------------------------------------------------------------

        ' Ignore ConfigMgr "ping" requests

        If PXE.IPAddress = "127.0.0.1" or PXE.MacAddress = "FF:FF:FF:FF:FF:FF" then
                PXE.LogTrace "Ignoring ConfigMgr ping request"
                Exit Function
        End if

        '//----------------------------------------------------------------------------
        '//  Verify the computer is known to ConfigMgr
        '//----------------------------------------------------------------------------

        ' Connect to the SMS provider

        Set oLocator = CreateObject("WbemScripting.SWbemLocator")
        Set oSMS = oLocator.ConnectServer(sProviderServer, sNamespace, sUsername, sPassword)

        ' Build the query

        sQuery = "SELECT * FROM SMS_R_System WHERE MacAddresses = '" & sMacAddress & "'"
        If sUUID <> "" then
                sQuery = sQuery & " OR SMBIOSGUID = '" & sUUID & "'"
        End if

        ' Process the query

        bFound = False
        Set oClients = oSMS.ExecQuery(sQuery)
        For each oClient in oClients
                bFound = true
                iResourceID = oClient.ResourceID
                PXE.LogTrace "Found existing machine " & oClient.NetbiosName & " with ResourceID = " & iResourceID
                sNetBiosName = oClient.NetbiosName
                Exit For
        Next

        '//----------------------------------------------------------------------------
        '//  If necessary, add the computer to the ConfigMgr database
        '//----------------------------------------------------------------------------

        If not bFound then 

                PXE.LogTrace "Could not find machine with MAC address '" & sMacAddress & "' or SMBIOS UUID '" & sUUID & "'."

                ' Add the computer

                Set oSite = oSMS.Get("SMS_Site")
                Set oParams = oSite.Methods_.Item("ImportMachineEntry").inParameters.SpawnInstance_()
                oParams.NetbiosName = sNetBiosName
                oParams.SMBIOSGUID = sUUID
                oParams.MACAddress = sMacAddress
                oParams.OverwriteExistingRecord = false

                On Error Resume Next
                Set oResult = oSite.ExecMethod_("ImportMachineEntry", oParams)
                If Err then
                        PXE.LogTrace "Error importing machine entry: " & Err.Description & " (" & Err.Number & ")"
                        Set oLastError = CreateObject("WbemScripting.SWbemLastError")
                        PXE.LogTrace "Last WMI error: " & oLastError.Description
                        Exit Function
                End if
                On Error Goto 0

                iResourceID = oResult.ResourceID
                PXE.LogTrace "Added new machine with ResourceID = " & iResourceID

        End if

        '//----------------------------------------------------------------------------
        '//  Check if the computer is a member of the specified collection
        '//----------------------------------------------------------------------------

        If bFound then

                ' Build the query

                sQuery = "SELECT * FROM SMS_CM_RES_COLL_" & sCollection & " WHERE ResourceID = " & iResourceId

                ' Process the query

                bFound = False
                Set oClients = oSMS.ExecQuery(sQuery)
                For each oClient in oClients
                        bFound = true
                        PXE.LogTrace "Machine is already in collection " & sCollection
                        Exit For
                Next

        End If

        '//----------------------------------------------------------------------------
        '//  If necessary, add the computer to the specified collection
        '//----------------------------------------------------------------------------

        If not bFound then

                ' Add the computer to the specified collection

                On Error Resume Next
                Set oCollection = oSMS.Get("SMS_Collection.CollectionID='" & sCollection & "'")
                If Err then
                        PXE.LogTrace "Error retrieving collection " & sCollection & ": " & Err.Description & " (" & Err.Number & ")"
                        Set oLastError = CreateObject("WbemScripting.SWbemLastError")
                        PXE.LogTrace "Last WMI error: " & oLastError.Description
                        Exit Function
                End if
                On Error Goto 0

                Set oNewRule = oSMS.Get("SMS_CollectionRuleDirect").SpawnInstance_()
                oNewRule.ResourceClassName = "SMS_R_System"
                oNewRule.RuleName = sNetBiosName
                oNewRule.ResourceID = iResourceID

                On Error Resume Next
                oCollection.AddMembershipRule oNewRule
                If Err then
                        PXE.LogTrace "Error adding membership rule to " & sCollection & ": " & Err.Description & " (" & Err.Number & ")"
                        Set oLastError = CreateObject("WbemScripting.SWbemLastError")
                        PXE.LogTrace "Last WMI error: " & oLastError.Description
                        Exit Function
                End if
                On Error Goto 0
                PXE.LogTrace "Added new membership rule to collection " & sCollection

        End if

        '//----------------------------------------------------------------------------
        '//  Wait until an advertisement is seen.
        '//----------------------------------------------------------------------------

        ' Check for the advertisements

        Set oAdvertisement = oSMS.Get("SMS_Advertisement")
        i = 0
        bTrying = True
        Do While bTrying 

                Set oParams = oAdvertisement.Methods_("GetAdvertisements").inParameters.SpawnInstance_()
                oParams.ResourceID = iResourceID

                Set oResult = oAdvertisement.ExecMethod_("GetAdvertisements", oParams)
                For each sAdvert in oResult.AdvertisementIDs
                        PXE.LogTrace "Found advertisement " & sAdvert
                        bTrying = False
                Next

                i = i + 1
                If bTrying and i > 10 then
                        PXE.LogTrace "Giving up after 30 seconds of checking for new advertisements."
                        bTrying = False
                End if

                PXE.Sleep 3000

        Loop

        PXE.LogTrace "Exiting PXEFilter.vbs"

End Function