Lenovo and BIOS Settings

Lenovo has provided numerous series of devices, which apparently do not have an aligned standard of handling BIOS / UEFI settings or BIOS / UEFI upgrades. As part of this headache and to ease the burden of jumping between the different device types I started to dive in to harmonize our scripts. As previously written Lenovo has a few odd design decisions – you can for example not reduce security via script. To disable TPM or SecureBoot you would be required to alter this via a keyboard. However, setting the initial password (increasing the security) also requires that you do this by manually typing it into the settings. In addition to the above challenges I reached the following conclusion;

1. It is most likely only relevant to enable any setting.
2. Handling passwords stinks (setting them etc)
3.  Settings between different models in a series can vary by name and vary by name of the possible values to set
4. WMI-classes used differs between series (ThinkStation, ThinkPad and ThinkCentre)

For example, ThinkPad has the GetBIOSSelections class, which can present the different possibilities for a specific setting. However, ThinkCentre does not have this class, but instead present options via a [bracket] indicator in the output of the currentsetting.

How would one deal with all this mess?

Two functions are provided – one for figuring our howto enable a specific setting. The other one for setting each setting. Its ugly – as the more models, bios-versions and whatnot I get a hold of the uglier variations of this I find. The initial attempt was to keep it as generic as possible.The best way to explain what I have todo sometimes;

 

Function Get-LenovoBIOSSettingValue {
    param(
        [Alias("BIOSSetting")]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Array]$Setting
        
    ) 

    begin {
            $complex = $null
            if(Get-WmiObject -namespace root\wmi -List | where { $_.Name -eq "Lenovo_GetBiosSelections"})
            {
                      $complex = $false
                      Write-verbose "Lenovo GetBiosSelections does exist"
            }
            else
            {
                      $complex = $true
                      Write-verbose "Lenovo GetBiosSelections does not exist"
            }
    }

    process {
            foreach ($s in $setting) {
                $value = $null
                $selections = $null
                $findsetting = $null
        
                If ($complex -eq $true) {
                    $findsetting = gwmi -class Lenovo_BiosSetting -namespace root\wmi | Where-Object {$_.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries) -like $s} | Select-Object CurrentSetting
                    try {
                        $value = $findsetting.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries)[0]
                    }
                    catch {
                       # write-output "Unable to convert $s to value"
                    }                        
                    $r = [regex] '(?<=\[).+?(?=\])'
                    try {

                         [string]$checkvalue = $findsetting.CurrentSetting.split(",",[StringSplitOptions]::RemoveEmptyEntries)[1].split(";",[StringSplitOptions]::RemoveEmptyEntries)[0]
                         $appliedselection = $checkvalue
                        if ([string]$($findsetting.CurrentSetting.split(";",[StringSplitOptions]::RemoveEmptyEntries)[1]) -Match 'Status:ShowOnly') {


                            if($checkvalue -like  "Active*" -or $checkvalue -like  "Enabl*"  -or $checkvalue -like 'Discrete*TPM*' ) 
                            {
                                    if ($value -like 'Security*Chip*') {
                                            $value = $null
                                            $selections = $null
                                            $findsetting = $null
                                    }
                                
                            }
                            else {
                                $value = $null
                                $selections = $null
                                $findsetting = $null
                            }
                        }
                    }
                    catch {
                        #noerror
                    }
                    $selections = ($r.match($findsetting.currentsetting).value).split(":").split(",") | Where-Object {$_ -like "Active*" -or $_ -like "Enabl*" -or $_ -like 'Discrete*TPM*' }
                    
                    if ($selections -is [array] -and $selections -contains 'Enable') {
                        $selections = $selections| Where-Object { $_ -like "Enabl*" }
                    }
    
                }
                else {

                    
                    try {
                        $findsetting = Get-WmiObject -Class Lenovo_BiosSetting -Namespace root\WMI | Where-Object {$_.CurrentSetting -match $s} | Select-Object CurrentSetting
                        $value = $findsetting.currentsetting.split(",")[0]
                        $appliedselection = $findsetting.currentsetting.split(",")[1]
                    }
                    catch {
                        #write-output "Unable to convert $s to value"
                    }

                    $selections = ((gwmi –class Lenovo_GetBiosSelections –namespace root\wmi).GetBiosSelections($value).selections).split(",") | Where-Object {$_ -like "Active*" -or $_ -like "Enabl*" }
                    if ($selections -is [array] -and $selections -contains 'Enable') {
                        $selections = $selections| Where-Object { $_ -like "Enabl*" }
                    }
                }
                if ($value -ne $null -and $selections -ne $null) {
                    $object = New-Object –TypeName PSObject
                    $object | Add-Member –MemberType NoteProperty –Name Setting –Value $($value)
                    $object | Add-Member –MemberType NoteProperty –Name Current –Value $($appliedselection)
                    $object | Add-Member –MemberType NoteProperty –Name Selection –Value $($selections) 
                    $object

                    #write-output "$s provided $value, which can be set to $selections"
                }
            }
    }
}

Function Set-LenovoBIOSSetting {
    param(
        [Alias("BIOSSetting")]
        [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [Array]$Setting,
        [Parameter(Position = 1, Mandatory = $false)]
        [String]$Password
        
    )

    begin {
        $wmi = Get-WmiObject -Class Lenovo_SetBiosSetting -Namespace root\wmi -ErrorAction Stop
        $object = $null
        if ($object) {
            Remove-Variable -Name $object
        }
        #$setting
    }

    process {
    
     
        foreach ($s in $setting) {
        #$s
            $result = $null
                 if ($s.Current -ne $s.Selection) {
                       $result = ($wmi.SetBiosSetting("$($s.setting),$($s.selection)$password")).return
                        $object = New-Object –TypeName PSObject
                        $object | Add-Member –MemberType NoteProperty –Name Setting –Value $($s.setting)
                        $object | Add-Member –MemberType NoteProperty –Name Current –Value $($s.selection)
                        $object | Add-Member –MemberType NoteProperty –Name Result –Value $result
                        $object
                 }
                 else {

                        $object = New-Object –TypeName PSObject
                        $object | Add-Member –MemberType NoteProperty –Name Setting –Value $($s.setting)
                        $object | Add-Member –MemberType NoteProperty –Name Current –Value $($s.Current)
                        $object | Add-Member –MemberType NoteProperty –Name Result –Value 'Not updated'
                        $object
                }
         }
    }

}

Note to self: WMI does not adhere to RPC-standards

Every single project…. every single firewall guy, and every single requirement list that I had to dig into…

Windows Management Traffic leverages the same ports as RPC traffic (TCP 135 for initial connection, and after that a random port within a defined port-range), however it does not adhere to the RPC specification and will therefore not be correctly identified by any firewall (yes, any firewall) as RPC traffic. Most firewalls tries to dynamically identify the specific port for the session within the dynamic range, however this requires that lots of things are RPC and not MSRPC.

Cisco wrote it pretty clearly;

As Microsoft switched from using pure RPC to use DCOM (ORPC) calls, those non-epm calls will be used more and more. Windows RPC/DCOM services use the RPC Endpoint Mapper to accept initial communications on port 135 and then dynamically transition to ports for the service.

Just open all the high-ports.

Checkpoint statement

Cisco statement

Troubleshoot:

Testing RPC ports with PowerShell (and yes, it’s as much fun as it sounds)

Wireshark-article if you ever need to troubleshoot

 

Lenovo and management of BIOS settings

Lenovo has published an excellent guide for management of their BIOS settings via scripts for the ThinkPad-series. It seems that it does apply for all different series (ThinkCentre, ThinkPad, ThinkStation) and therefore the same methods can be used regardless of the type of device.

However, there are numerous caveats to the documentation and some minor misalignments of naming standards between specific device types – even within the same series of devices.

Supervisor Password

Initial password

Password seems to be quite odd for Lenovo. First of all – password can’t be set the first time around via their WMI interface but requires that someone sets on the device. In addition – the experience has been that depending on the type of keyboard (validated with a Lenovo and an HP set of keyboards) the password might not be set as expected. In the end – we could only validate what the password was (and use the expected password) when set with an HP-keyboard.

Updates settings with password

Once a password is set it becomes a requirement to pass this one when changing any setting, or setting a new password. To pass this one each updated setting requires the password, encoding and keyboard and in addition it is also required when saving the bios settings. One could find many more efficient methods, but this is the way togo about it. After lots of testing – the following methods have been succesful;

Changing a BIOS configuration

$wmi = Get-WmiObject -Class Lenovo_SetBiosSetting -Namespace root\wmi
$wmi.SetBiosSetting("TCG Security Feature,Active,password,ascii,us;")

Note that at the end there is a ;.
Ascii and us is the encoding and the language of the keyboard. This is the most common setup – so lets stick with it.

To save the settings the following command can be issued;

(Get-WmiObject -Class Lenovo_SaveBiosSettings -Namespace root\wmi).SaveBiosSettings(password,ascii,us;)

If you are unsure wether there is a password or not – we can always test and validate. If you configure all settings incorrectly and then try to save without the appropiate password (blank or with the correct password) – all settings are lost.

You can check if a password is set by using the following method;

$password = “,password,ascii,us”
$result = ((Get-WmiObject -Class Lenovo_SaveBiosSettings -Namespace root\wmi).SaveBiosSettings(password,ascii,us)).return
if (!($result –eq "Success")) {
#if the command isn’t successfull we set a blank password
$password = ""
}
if ($result –eq "Success") {
$nopass = $false
}

To avoid writing lots of code once we have identified if a password is in use – we can leverage the $password and append it to every settings.

$wmi.SetBiosSetting("TCG Security Feature,Active$password")

The $nopass can be used to choose decide how we save the settings

if ($nopass -eq $true) {
Get-WmiObject -Class Lenovo_SaveBiosSettings -Namespace root\wmi).SaveBiosSettings()
}
else {
(Get-WmiObject -Class Lenovo_SaveBiosSettings -Namespace root\wmi).SaveBiosSettings($passwordsave)
}
<pre>[sourcecode]

Settings

Unlike the harmonized and common way to handle BIOS settings via the WMI interface – settings have a wide spread of possible names and setting options. Quite often similiar enough to cause frustration

An overview of TPM related settings and Secureboot

image

In addition – once these settings are enabled they can’t be disabled. Lenovo has taken a secure-by-default stance and will force someone to physically access the computer to decrease security. As far as their guide states today the following settings can’t be disabled – once they are enabled – via WMI.

SecureBoot
SecureRollbackPrevention
PhysicalPresneceForTpmClear
PhysicalPresenceForTpmProvision

WMI Hotfixes for Windows 7 x64

##################################################
WMI
##################################################
You cannot overwrite an exported event log file by using the Wevtutil.exe tool in Windows 7, Windows Server 2008 R2, Windows 8, and Windows Server 2012
http://support.microsoft.com/kb/2797789/en-us
Files Updated:
Wevtsvc.dll
6.1.7601.22213
1,650,176
09-Jan-2013

Profile loading takes a long time due to full DFS namespace sync with PDC
http://support.microsoft.com/kb/2915094/en-us

Files Updated:
Profprov.dll
6.1.7601.22575
33,792
18-Jan-2014

Profsvc.dll
6.1.7601.22575
225,280
18-Jan-2014

Profsvc.ptxml
Not applicable
648
13-Jul-2009

Userprofilewmiprovider.mof
Not applicable
10,708
13-Jul-2009

High memory usage by the Svchost.exe process after you install Windows Management Framework 3.0 on a Windows-based computer
http://support.microsoft.com/kb/2889748
Files Updated:
Wmidcprv.dll
6.2.9200.16398
180,736
04-Jul-2013

Wmiprvsd.dll
6.2.9200.16706
724,992
26-Sep-2013

Wmiprvse.exe
6.2.9200.16398
432,128
04-Jul-2013

Wmidcprv.dll
6.2.9200.16398
180,736
09-Jul-2013

Wmiprvsd.dll
6.2.9200.20813
724,992
26-Sep-2013

Wmiprvse.exe
6.2.9200.16398
432,128
09-Jul-2013

Wmidcprv.dll
6.2.9200.16398
129,536
26-Sep-2013

Wmiprvse.exe
6.2.9200.16398
328,704
26-Sep-2013

Wmidcprv.dll
6.2.9200.16398
129,536
26-Sep-2013

Wmiprvse.exe
6.2.9200.16398
328,704

An update that prevents a “0xC0000034” error message when you try to install Windows 7 SP1, Windows Server 2008 R2 SP1, or Windows Embedded Standard 7 SP1 is available
http://support.microsoft.com/kb/2533552/en-us
Alot of files are updated

Forwarded events cannot be displayed in Event Viewer on a Windows 7 or Windows Server 2008 R2-based computer
http://support.microsoft.com/kb/2794427/en-us
Files Updated:
Wevtsvc.dll
6.1.7601.22213
1,650,176
09-Jan-2013

Wmiprvse.exe process crashes when you run a WMI script on a computer that is running Windows 7 or Windows Server 2008 R2
http://support.microsoft.com/kb/2833001/en-us
Files Updated:
Cimwin32.dll
6.1.7601.22296
2,059,264
10-Apr-2013

Unexpectedly slow startup or logon process in Windows Server 2008 R2 or in Windows 7
http://support.microsoft.com/kb/2617858
Files Updated:
Repdrvfs.dll
6.1.7601.21824
453,632
21-Sep-2011

An application or service that queries information about a failover cluster by using the WMI provider may experience low performance or a time-out exception
http://support.microsoft.com/kb/974930
Files Updated:
Clussvc.exe
6.1.7600.20517
4,579,840
28-Aug-2009

Cluswmi.dll
6.1.7600.20517
540,160
28-Aug-2009

Cluswmi.mof
Not Applicable
76,540
28-Aug-2009

Cluswmiuninstall.mof
Not Applicable
176
13-Jul-2009