The case of Internet Explorer missing

I’ve updated recently a test server from Windows 2012 datacenter core edition to Windows 2012 R2.
Although I chose the GUI version during the upgrade, it didn’t change anything.

After the upgrade I moved from my core edition to the GUI version by following my previous article on “Switching from Windows 2012 Core to GUI mode…hands on!

I got my Start button back 🙂 but Internet explorer 11 still appeared to be missing although I restored all the GUI features 😦

(Get-WindowsFeature) | ? Name -match "internet"

Internet Explorer 11 on Windows 2012 R2 isn’t a feature or a role:

It’s actually an optional feature:

Get-WindowsOptionalFeature -Online | ? "FeatureName" -match "internet"

To restore Internet Explorer 11, I did

Get-WindowsOptionalFeature -Online |             
 ? "FeatureName" -match "Internet-Explorer-Optional-amd64" |            
 Enable-WindowsOptionalFeature -NoRestart:$true -Verbose -Online

Defeat the new Oracle Java Runtime(JRE) 1.7 update notification mechanism

You may have already seen this popup when your Oracle Java Runtime (JRE) 1.7 is out-of-date.

Your administrator may have already turned off the first mecanism of update using group policies to prevent update notifications’ balloons to pop out in the system tray.

Guess what! Oracle introduced a 2nd update notification mechanism since version 1.7_10 that phones home to check whether it’s up-to-date or expired.
If it’s unable to phone home, the expiry date is set using an hardcoded value that follows the CPU (Critical Patch Update) lifecycle. 😦

JRE Expiration Date
The JRE relies on periodic checks with an Oracle Server to determine if it (the JRE)is still considered up-to-date with all the available security fixes (above the security baseline). In the past, if the JRE was unable to contact the Oracle Server, it continued to behave as though it is still the most recent version with regard to security, for an indefinite period.
To avoid this problem, a secondary mechanism, that does not rely on external communication, has been added to the JDK 7u10. From this release onwards, all JREs will contain a hard-coded expiration date. The expiration date is calculated to end after the scheduled release of the next Critical Patch Update.
This means that JREs that are unable to contact Oracle Servers for an extended period of time, will now start offering additional protection after a reasonable period, and will not continue to behave as if they were still up-to-date with security fixes.

Sources: http://docs.oracle.com/javase/7/docs/technotes/guides/jweb/client-security.html#secure and http://www.oracle.com/technetwork/java/javase/7u10-relnotes-1880995.html

No, you’re not dreaming. We’re in 2013 and software developpers at Oracle launched this security awareness campaign using the above pop-up 😉
The problem with this new feature is that Oracle didn’t provide guidance on how to turn off this notification in a corporate environment.
You can of course click “Later” and tick “don’t ask again until next update” during a certain period of time. This will write some values in the registry and the current user deployment.properties file located in “%userprofile%AppData\LocalLow\Sun\Java\Deployment”. (Note that registry settings take precedence over the deployment.properties file content)

Worse, when the expiry date is reached, the pop-up doesn’t rely anymore on the above registry settings.
They are simply ignored and you start being nagged by this pop-up every time an applet is loaded by the browser.

Here’s what I asked on twitter to Donald Smith who is a member of the Oracle Java SE PM team:

The current situation is that we, I mean corporate administrators, have great hopes and may expect the JRE to really take fully advantages of Group Policies in a close future.

Source: http://www.symantec.com/connect/forums/how-everyone-addressing-forced-java-dialog-java-update-needed-your-java-version-insecure

As you can see, Donald was very kind and answered my questions very quickly.

But, as a corporate administrator, I don’t deploy and manage software with hope.

Now, let’s see how to defeat this new update notification feature.

The idea is simple. I’ll just prevent the DLL file responsible for these notifications from being loaded in Internet Explorer.

To achieve this, I need to identify the GUID (Global Unique identifiers) of the component and set its kill-bit value to COMPAT_EVIL_DONT_LOAD.

Let me reintroduce 3 small helper functions I wrote last year and that I revisited:

Function Get-KillBit {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[ValidatePattern('^\{[A-Z0-9]{4}([A-Z0-9]{4}-){4}[A-Z0-9]{12}\}$')]
[string[]]$GUID
)
Begin{
    $x86='Software\Wow6432Node'
    $x64='Software'
    $FLAGS = DATA {                        
        ConvertFrom-StringData @'
            1=COMPAT_AGGREGATE
            2=COMPAT_NO_OBJECTSAFETY
            4=COMPAT_NO_PROPNOTIFYSINK
            8=COMPAT_SEND_SHOW
            16=COMPAT_SEND_HIDE
            32=COMPAT_ALWAYS_INPLACEACTIVATE
            64=COMPAT_NO_SETEXTENT
            128=COMPAT_NO_UIACTIVATE
            256=COMPAT_NO_QUICKACTIVATE
            512=COMPAT_NO_BINDF_OFFLINEOPERATION
            1024=COMPAT_EVIL_DONT_LOAD
            2048=COMPAT_PROGSINK_UNTIL_ACTIVATED
            4096=COMPAT_USE_PROPBAG_AND_STREAM
            8192=COMPAT_DISABLEWINDOWLESS
            16384=COMPAT_SETWINDOWRGN
            32768=COMPAT_PRINTPLUGINSITE
            65536=COMPAT_INPLACEACTIVATEEVENWHENINVISIBLE
            131072=COMPAT_NEVERFOCUSSABLE
            262144=COMPAT_ALWAYSDEFERSETWINDOWRGN
            524288=COMPAT_INPLACEACTIVATESYNCHRONOUSLY
            1048576=COMPAT_NEEDSZEROBASEDDRAWRECT
            2097152=COMPAT_HWNDPRIVATE
            4194304=COMPAT_SECURITYCHECKONREDIRECT
            8388608=COMPAT_SAFEFOR_LOADING
'@        
    }
}
Process{
    $GUID | ForEach-Object -Process {
        $GUIDitem = $_
        Write-Verbose "Testing GUID $GUIDitem"
        $x86,$x64 | ForEach-Object {
            Write-Verbose "Testing Hive $_"
            $RegPath = $_
            $flag = $null
            if (Test-Path "HKLM:\$RegPath\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem") {
                try {
                    $flag = Get-ItemProperty -Path "HKLM:\$RegPath\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem" -Name 'Compatibility Flags' -ErrorAction Stop
                } catch {
                    Write-Warning "Failed because $($_.Exception.Message)"
                    return
                }
                $Meaning = @()
                $FLAGS.Keys | ForEach-Object {
                    if (($flag.'Compatibility Flags') -band $_) {
                        $Meaning += $FLAGS["$_"]
                    }
                }
                New-Object -TypeName PSobject -Property @{
                    Meaning = $Meaning
                    GUID = $GUIDitem
                    Path = $flag.PSPath
                    Value = $flag.'Compatibility Flags'
                    HexValue = -join ('0x',('{0:X0}' -f $flag.'Compatibility Flags'))
                    DisplayName = (Get-ItemProperty -Path "HKLM:\$RegPath\Classes\CLSID\$GUIDitem" -Name '(default)' -ErrorAction Silentlycontinue).'(default)'
                }
            } else {
                Write-Verbose "Skipped HKLM:\$RegPath\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem as it doesn't exist"
            }
        }
    }
}
End{}
}

Function Set-KillBit {            
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[ValidatePattern('^\{[A-Z0-9]{4}([A-Z0-9]{4}-){4}[A-Z0-9]{12}\}$')]
[string[]]$GUID
)
Begin {
    # Make sure we run as admin
    $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
    $IsAdmin = $usercontext.IsInRole(544)
    if (-not($IsAdmin)) {
        Write-Warning "Must run powerShell as Administrator to perform these actions"
        return
    }
}
Process {
    $GUID | ForEach-Object -Process {
        $GUIDitem = $_
        Write-Verbose "Handling GUID $GUIDitem"
        $null,"Wow6432Node" | ForEach-Object {
            $HiveLocation = $_
            $flagged = $null
            try {
                if (-not(Test-Path "HKLM:\Software\$HiveLocation\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem")) {
                    New-Item -Path "HKLM:\Software\$HiveLocation\Microsoft\Internet Explorer\ActiveX Compatibility" -Name $GUIDitem -Force -ErrorAction Stop | Out-Null
                    New-ItemProperty -Path "HKLM:\Software\$HiveLocation\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem" -PropertyType DWORD -Name 'Compatibility Flags' -Value 1024 -Force | Out-Null
                } else {
                    Set-ItemProperty -Path "HKLM:\Software\$HiveLocation\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem" -Name 'Compatibility Flags' -Value 1024 -Type DWORD -Force -ErrorAction Stop
                }
                $flagged = $true
            } catch {
                $flagged = $false
                Write-Warning "Failed because $($_.Exception.Message)"
            }
            if ($flagged) {
                Write-Verbose -Message "Successfully set kill-bit on $GUIDitem in hive $HiveLocation"
            } else {
                Write-Verbose -Message "Failed to set kill-bit on $GUIDitem in hive $HiveLocation"
            }
        }
    }
}
End{}
}

Function Clear-KillBit {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[ValidatePattern('^\{[A-Z0-9]{4}([A-Z0-9]{4}-){4}[A-Z0-9]{12}\}$')]
[string[]]$GUID
)
Begin {
    # Make sure we run as admin
    $usercontext = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()
    $IsAdmin = $usercontext.IsInRole(544)
    if (-not($IsAdmin)) {
        Write-Warning "Must run powerShell as Administrator to perform these actions"
        return
    }
}
Process {
    $GUID | ForEach-Object -Process {
        $GUIDitem = $_
        Write-Verbose "Handling GUID $GUIDitem"
        $null,"Wow6432Node" | ForEach-Object {
            Write-Verbose "Setting it in hive $_"
            $HiveLocation = $_
            $flagged = $null
            try {
                if (Test-Path "HKLM:\Software\$HiveLocation\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem") {
                    Remove-Item -Path "HKLM:\Software\$HiveLocation\Microsoft\Internet Explorer\ActiveX Compatibility\$GUIDitem" -Force -ErrorAction Stop | Out-Null
                } else {
                    # already removed
                }
                $flagged = $true
            } catch {
                $flagged = $false
                Write-Warning "Failed because $($_.Exception.Message)"
            }
            if ($flagged) {
                Write-Verbose -Message "Successfully cleared kill-bit on $GUIDitem in hive $HiveLocation"
            } else {
                Write-Verbose -Message "Failed to clear kill-bit on $GUIDitem in hive $HiveLocation"
            }
        }
    }
}
End{}
}

I found the meaning of the decimal/hexadecimal values on this MSDN page where it’s also mentioned that

These enumeration members are bit masks that determine how ActiveX controls are used in Internet Explorer.

My Set-KillBit function only uses the single value “COMPAT_EVIL_DONT_LOAD”.
It doesn’t “merge” bit mask values although it should and note that it could overwrite any preexisting values, so use it with caution.
Idem, my Clear-KillBit function doesn’t remove the 0x400 flag.
Only my Get-KillBit function isn’t considered as written in quick and dirty mode 😛

  • Step 1: Identify GUIDs
  • $allJREGUIDs = @()            
    $Nodes = @($null,'Wow6432Node')            
    $WMI = [wmiclass]"root\default:stdRegProv"            
    $allJREGUIDs += $Nodes | ForEach-Object {             
        $WMI.EnumKey(2147483650,"SOFTWARE\$_\Classes\CLSID").sNames            
    } | Where-Object { $_ -match "CAFEEFAC"}             
    $allJREGUIDs | ? {$_ -match "-DEC"}
  • Step 2: Set the kill-bit on filtered JRE GUIDs, only those related to the “Deployment Toolkit”
  • $allJREGUIDs | ? {$_ -match "-DEC"} | Set-KillBit -Verbose
  • Step 3: Check what was set
  • $allJREGUIDs | ? {$_ -match "-DEC"} | Get-KillBit |            
     ft GUID,DisplayName,Meaning            
    

To test, you need 4 things:

  • an out-of-date JRE version
  • a test URL
  • http://www.java.com/en/download/testjava.jsp

  • A way to revert back to the original state with no kill-bit set on the deployment kit component
  • $allJREGUIDs | ? {$_ -match "-DEC"} | Get-KillBit |            
     Clear-KillBit -Verbose
  • Change your system clock to 31 days after the next CPU planned for 15 October 2013, i.e., after the 15th of November

Happy testing until Oracle fixes its products and releases an official guidance about the new notification, a.k.a JRE expiration, feature 😎

What processes are responsible for the low memory warning?

I’ve a VM where I had the following warning:

To find the culprit using the event logs I did:

# Get the latest event            
[xml]$xml  = (Get-WinEvent -FilterHashtable @{            
    LogName='System';            
    ProviderName='Microsoft-Windows-Resource-Exhaustion-Detector';            
} -MaxEvents 1).ToXML()            
            
# Same console ouput as | fl *            
$xml.Event.UserData.MemoryExhaustionInfo.ProcessInfo.GetEnumerator()            
            
# Get a nice display            
$xml.Event.UserData.MemoryExhaustionInfo.ProcessInfo.GetEnumerator()|            
ft -Property Name,ID,HandleCount,@{            
    l='CommitCharge (MB)';            
    e={'{0:N2}'-f($_.CommitCharge/1MB)}            
}            

Here’s the nice output 😎

So, nothing unusual or malicious…

Create an external VM switch in Hyper-V

This morning I wanted to quickly add an External Hyper-V switch in my lab.
I’ve already added an Internal switch but as I needed to get access to Internet, an External switch was required.

Without reading the help, I naturally did:

New-VMSwitch -Name External -SwitchType External

Even if the External value was technically allowed by tab completion, the following error reminded me how stupid was my above assumption.
New-VMSwitch : Cannot validate argument on parameter ‘SwitchType’. The argument “External” does not belong to the set
“Internal,Private” specified by the ValidateSet attribute. Supply an argument that is in the set and then try the
command again.

Of course, to get access to Internet or to the same network as the physical nic, the External switch needs to be bound to a physical network card!

To help also states:

-SwitchType
Specifies the type of the switch to be created. Allowed values are Internal and Private. To create an External
virtual switch, specify either the NetAdapterInterfaceDescription or the NetAdapterName parameter, which
implicitly set the type of the virtual switch to External.

Then I remembered that I already did this operation some months ago where I had only one physical adapter:

New-VMSwitch -Name Prod -NetAdapterName ((Get-NetAdapter|            
? Status -eq "Up")[0].Name)

I couldn’t use exactly the same above command I previously used because the firt item returned by the filtered Get-NetAdapter” cmdlet was the Internal switch I previously set.

As you can see, filtering with the ‘Status’ wasn’t enough. I decided to compare the properties between the 2 network cards instances returned by the Get-NetAdapter cmdlet.

I quickly did…

$a =  (Get-NetAdapter  | ? Status -eq "Up")[0]            
$b =  (Get-NetAdapter  | ? Status -eq "Up")[1]            
Compare-Object -ReferenceObject $a -DifferenceObject $b

…and couldn’t get the result I expected 😦

Comparing instances of WMI objects isn’t as trivial as it seems to be. Here’s the quick and dirty thing I did:

$a =  (Get-NetAdapter  | ? Status -eq "Up")[0]            
$b =  (Get-NetAdapter  | ? Status -eq "Up")[1]            
# Fill-in an array with properties that have a value            
$props = @()            
($a | gm -MemberType Property).Name | % {            
    if (($a.$_)|Out-String) {             
        $props += $_             
    }            
}            
# Compare properties            
$props | % {             
 Compare-Object -Ref $a -Diff $b -Property $_ | Out-String            
}

After that, I found the NdisPhysicalMedium that seems promising and able to differentiate the Internal switch and the physical Nic of the Hyper-V host.
I found the meaning of the different values on http://www.powershellmagazine.com/2013/04/04/pstip-detecting-wi-fi-adapters

To install my External VM switch, I just extended the filter and did:

New-VMSwitch -Name External -NetAdapterName (            
Get-NetAdapter |?{            
 $_.Status -eq "Up" -and $_.NdisPhysicalMedium -eq 14             
 }).Name

About keyboard layouts

A colleague from the helpdesk team recently asked if I could report the keyboad layout set before users log onto the computer.

I started digging into WMI classes by typing

Get-CimClass -ClassName *Keyboard*

Then did:

Get-CimInstance Win32_Keyboard

… and noticed the Layout property.

Both the MSDN page about the WMI Win32_Keyboard class…

…and the following powershell commands…

([wmiclass]'Win32_Keyboard').GetText("MOF")            
([wmiclass]'CIM_Keyboard').GetText("MOF")

…confirmed that the layout property is returned as a string value.

The problem with this value is that it’s not human readable and represents actually a hexadecimal value.
Although I can find the mapping of hexadecimal values to a user friendly value in the following registry key HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Keyboard Layout\DosKeybCodes, I decided to go another way.

First, do you know that you can use DISM.exe locally:

Dism /online /Get-Intl


As you can see, it displays both the keyboard and input language. The input language is 407 (German) and the keyboard layout is 809 (English UK). This corresponds to what you find under the HKU\.DEFAULT\Keyboard Layout registry key:

As you can see, DISM is precise but doesn’t really help on the readability issue.
Here’s what I propose that is based on the Win32_Keyboard WMI Class:

#Requires -Version 2            
Function Get-KeyboardLayout {            
[CmdletBinding()]            
Param(            
[Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipeLineByPropertyName=$true)]            
[Alias('CN','__Server','IPAddress','Server','hostname')]            
[string[]]$ComputerName=".",            
             
[parameter()]            
[Alias('RunAs')]            
[System.Management.Automation.Credential()]$Credential = [System.Management.Automation.PSCredential]::Empty            
)            
Begin {}            
Process {            
  $ComputerName | ForEach-Object -Process {            
                     
        $Computer = $_            
        Write-Verbose -Message "Processing computer $Computer"            
                     
        # Prepare a hashtable for splatting            
        $WMIHT = @{            
            ErrorAction = "Stop" ;            
            ComputerName = $Computer ;            
        }            
                     
        # Add creds to hastable...            
        if ($PSBoundParameters.ContainsKey('Credential')) {            
            Write-Verbose "Adding Credentials to hashtable"            
            $WMIHT += @{ Credential = $Credential}            
        }            
        # ...but avoid WMI error: User credentials cannot be used for local connections            
        'localhost','.','127.0.0.1','::1',$env:COMPUTERNAME | ForEach-Object {            
            if ($Computer -eq $_) {            
                Write-Verbose "Removing credentials as localhost is targeted"            
                $WMIHT.Remove('Credential') | Out-Null            
                $WMIHT.Remove('ComputerName') | Out-Null            
            }            
        }            
             
        try {            
            $ok = $true            
            $KB = Get-WmiObject -Class Win32_Keyboard -Property Layout @WMIHT            
            Write-Verbose -Message "Performing WMI query of computer $Computer"            
        } catch {            
            Write-Warning -Message "WMI query for computer $Computer failed because $($_.Exception.Message)"            
            $ok = $false            
        }            
                     
        # Send a PSObject through the pipeline            
        if ($ok) {            
            New-Object -TypeName PSObject -Property @{            
                PSComputerName = $Computer            
                Layout = [System.Globalization.CultureInfo]([int32]"0x$($KB.Layout)")            
                HexValue = $KB.Layout            
            }            
            Write-Verbose -Message "WMI query of computer $Computer completed"            
        }            
    }            
}            
End {}            
} # end of function            

Let’s see what this function does by turning on its verbose mode.

As you can see, the WMI Win32_Keyboard class reports the keyboard layout correctly for my localhost that had German as input language and English UK as keyboard layout.

Follow-up on Scripting Games 2013 Event 6


As you can see, MVP Richard Siddaway voted so far on more than 1000 script, WOW!!!!!
He left the following comment on my Event 6 entry:

Don’t get me wrong. I really appreciate the feedback. The Scripting Games are a unique occasion to get a review from your peers and most valuable persons, who are all part of the PowerShell community. Richard Siddaway is one of the judges in the Scripting Games 2013 that you can follow on twitter:

Source: http://powershell.org/wp/2013/04/06/2013-scripting-games-judges and http://powershell.org/wp/2013/04/06/2013-scripting-games-mighty-panel-of-celebrity-judges

Let’s get back to the comment of Richard Siddaway and here’s what I’d like to say:

@RichardSiddaway,

Hi, Thanks for the feedback. Please allow me to comment on event 6, answer your questions and explain the choices I made:

Well, the instructions don’t say what hypervisor we should use. I choose Hyper-V because I know it, because it’s installed by a single cmdlet. I could have used VMware but I don’t know it. I could have also used KVM but there isn’t any PowerShell module for it…To continue on the Hypervisor, the instructions don’t say what version, if it’s clustered, if it’s domain joined, if there are other virtual machines running,…

The instructions don’t say if the DHCP is running Windows, if it’s running Windows 2012 that has the built-in DHCP module, if it’s joined to the domain…

Yes, you’re right it would have been easier to use the wildcard for the trusted host. However it doesn’t sound as a good practice for me. I also wanted to have reusable code for handling trusted host additions and removals.

Yes, I’m sure that I can rename and join and reboot the computer the way I did. I started with Add-computer but had some weird issues. So, I switched over the above WMI approach. I’ve tested and it worked better than Add-computer. Add-computer has limited Fjoin flags whereas WMI hasn’t. In my testing, WMI appeared to be more reliable. I’ve a blog post about this https://p0w3rsh3ll.wordpress.com/2013/06/04/2013-scripting-games-event-6/

Event 6 was the hardest event of the Scripting Games. Currently, only Bartek Bielawski shared how he’d have done this event: http://becomelotr.wordpress.com/2013/06/05/event-6-my-way (I’m glad I’m not the only one who noticed that you can get IP and MAC addresses directly from Hyper-V running on Windows 2012 😀 )

Nobody asked or commented on it yet, but there are actually 2 reasons why I collected IP Addresses from Hyper-V in my entry:
First, I installed a few VMs, deleted them, installed a new serie,… but DHCP leases of dead VMs were still “active”. (My bad, yeah, I know, I’m not a DHCP guy 😛 )
The second reason was that I wanted to test the remoting with the built-in Test-WSMan cmdlet before trying to use Invoke-Command on the target VMs and join them to the domain.
So, to speed-up WSMan/PSremoting tests, I ended filtering the DHCP data with the active IPs I got from Hyper-V.

Desired State Configuration (DSC) in Powershell version 4


The title of the “super secret” session by Jeffrey Snover is actually: Desired State Configuration in Windows Server 2012 R2
…and it’s now publicly available, you can watch the video on Channel9

The 5 steps for a great automation system
… that you can find in the Monad Manifesto written in 2002.

What Desired Configuration State (DSC) looks like

What is DSC ?
Here’s the definition proposed by Jeffrey Snover @20:20

Desired State Configuration is a standard space management thing, first implemented on Windows, using a PowerShell language and WMI and a PowerShell extension model.

Declarative vs. Imperative code

How this really works? (start from the right)

Other related links from Don Jones