jump to navigation

Windows 7 Notification Area Automation – Falling Back Down the Binary Registry Rabbit Hole July 8, 2011

Posted by Micah Rowland in PowerShell, Windows 7.
trackback

Once more unto the breach dear friends.

After countless hours of searching for a programmatic way to modify the notification settings of the task tray icons, I came to the conclusion that there are many questions and no answers out there. I embarked once again on the fun task of reverse engineering a binary registry setting to change a setting that should be pretty straight forward but, alas, is not. So let’s dive in shall we:

The notification settings for the task tray are stored in the registry at HKCU\Software\Classes\Local Settings\Microsoft\Windows\CurrentVersion\TrayNotify in the IconStreams value as a binary registry key. Luckly for us, the organization of the key is not nearly as hard to understand as the Favorites Bar. The binary stream begins with a 20 byte header followed by X number of 1640 byte items where X is the number of items that have notification settings. Each 1640 byte block is comprised of at least (one of the sections is not fully decoded so it may be made up of 2 or more sections) 5 fixed byte width sections as follows:

  • 528 bytes – Path to the executable
  • 4 bytes – Notification visibility setting
  • 512 bytes – Last visible tooltip
  • 592 bytes - Unknown (Seems to have a second tool-tip embeded in it but the starting position in the block changes)
  • 4 bytes – ID?

For the purposes of my automation, the first two blocks will serve very nicely.

param(
    [Parameter(Mandatory=$true,HelpMessage='The name of the program')][string]$ProgramName,
    [Parameter(Mandatory=$true,HelpMessage='The setting (2 = show icon and notifications 1 = hide icon and notifications, 0 = only show notifications')]
        [ValidateScript({if ($_ -lt 0 -or $_ -gt 2) { throw 'Invalid setting' } return $true})]
        [Int16]$Setting
    )

$encText = New-Object System.Text.UTF8Encoding
[byte[]] $bytRegKey = @()
$strRegKey = ""
$bytRegKey = $(Get-ItemProperty $(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath).IconStreams
for($x=0; $x -le $bytRegKey.Count; $x++)
{
    $tempString = [Convert]::ToString($bytRegKey[$x], 16)
    switch($tempString.Length)
    {
        0 {$strRegKey += "00"}
        1 {$strRegKey += "0" + $tempString}
        2 {$strRegKey += $tempString}
    }
}
[byte[]] $bytTempAppPath = @()
$bytTempAppPath = $encText.GetBytes($ProgramName)
[byte[]] $bytAppPath = @()
$strAppPath = ""

Function Rot13($byteToRot)
{
    if($byteToRot -gt 64 -and $byteToRot -lt 91)
    {
        $bytRot = $($($byteToRot - 64 + 13) % 26 + 64)
        return $bytRot
    }
    elseif($byteToRot -gt 96 -and $byteToRot -lt 123)
    {
        $bytRot = $($($byteToRot - 96 + 13) % 26 + 96)
        return $bytRot
    }
    else
    {
        return $byteToRot
    }
}

for($x = 0; $x -lt $bytTempAppPath.Count * 2; $x++)
{
    If($x % 2 -eq 0)
    {
        $curbyte = $bytTempAppPath[$([Int]($x / 2))]
            $bytAppPath += Rot13($curbyte)

    }
    Else
    {
        $bytAppPath += 0
    }
}

for($x=0; $x -lt $bytAppPath.Count; $x++)
{
    $tempString = [Convert]::ToString($bytAppPath[$x], 16)
    switch($tempString.Length)
    {
        0 {$strAppPath += "00"}
        1 {$strAppPath += "0" + $tempString}
        2 {$strAppPath += $tempString}
    }
}
if(-not $strRegKey.Contains($strAppPath))
{
    Write-Host Program not found. Programs are case sensitive.
    break
}

[byte[]] $header = @()
$items = @{}
for($x=0; $x -lt 20; $x++)
{
    $header += $bytRegKey[$x]
}

for($x=0; $x -lt $(($bytRegKey.Count-20)/1640); $x++)
{
    [byte[]] $item=@()
    $startingByte = 20 + ($x*1640)
    $item += $bytRegKey[$($startingByte)..$($startingByte+1639)]
    $items.Add($startingByte.ToString(), $item)
}

foreach($key in $items.Keys)
{
$item = $items[$key]
    $strItem = ""
    $tempString = ""

    for($x=0; $x -le $item.Count; $x++)
    {
        $tempString = [Convert]::ToString($item[$x], 16)
        switch($tempString.Length)
        {
            0 {$strItem += "00"}
            1 {$strItem += "0" + $tempString}
            2 {$strItem += $tempString}
        }
    }
    if($strItem.Contains($strAppPath))
    {
        Write-Host Item Found with $ProgramName in item starting with byte $key
            $bytRegKey[$([Convert]::ToInt32($key)+528)] = $setting
            Set-ItemProperty $($(Get-Item 'HKCU:\Software\Classes\Local Settings\Software\Microsoft\Windows\CurrentVersion\TrayNotify').PSPath) -name IconStreams -value $bytRegKey
    }
}

So what are we doing here. First we ask for two commandline arguments, the name of the program (normally the executable of the program) which is case sensitive, and the setting value. The possible settings values are:

  • 0 = only show notifications
  • 1 = hide icon and notifications
  • 2 = show icon and notifications

We then read in the current value from the registry and store it in a byte array, then create a string representation of the byte array to search for the application in.

We then encode the application name supplied in both a byte array and a string as with the registry value, then we search for the application in the registry key. If not found, we throw an error and drop out. Otherwise we continue.

We then store the header in its own byte array (we don’t use this header but if we need it in the future, we’ve already got the code to segregate it). After that, we loop through the remaining bytes, 1640 bytes at a time, and store each block in it’s own array which in turn is placed in a hash table using the begining byte position as its key.

Finally we loop through each key and search again for the application. If it is found, we set the 529th byte equal to the setting value passed in and then write the byte array back into the registry.

The astute script reader and novice cryptographer will notice our old friend ROT13. It turns out that Microsoft didn’t want us playing with this key so they used the unbreakable ROT13 algorithm on its contents. ROT13 is a mathematical substitution cypher (it’s much simpler than it sounds) which advances each alpha character 13 letters forward so A=N B=O C=P D=Q and so on. I’ve created a function that handles this in a case sensitive way and call it as needed.

So we have our script, but there’s one last wrinkle. The IconStreams registry value is read into memory by Explorer.exe when explorer loads and all changes to the notification area are stored in memory, then written to the registry on shutdown. This means that if we run our script, not only will we see no results right away, those results will be overwritten with the current settings when we restart the computer. Not good. It’s a simple fix though. We launch our script from a batch file and we execute taskkill /im explorer.exe /f then we execute our script, then we restart explorer.exe.

That’s pretty much it. No need for rainbow diagrams of nested dynamically sized items this time.

I will be cleaning up the script and updating this post later with additional information (default user hive info). Stay tuned!

About these ads

Comments»

1. maske money online - April 19, 2013

Currently it appears like Movable Type is the preferred blogging platform available right now.
(from what I’ve read) Is that what you’re using on your blog?

2. Rolland - March 21, 2013

Have you ever considered about adding a little bit more than just
your articles? I mean, what you say is important and all.
But think about if you added some great visuals or video clips to give your posts more, “pop”!
Your content is excellent but with images and video clips,
this site could undeniably be one of the best in its
niche. Amazing blog!

3. jamescanon.com - March 13, 2013

I have read some excellent stuff here. Certainly worth bookmarking
for revisiting. I wonder how much effort you set to make one of these magnificent informative site.

4. Forcing notification area Icons to always show in Windows 7 or Windows 8 - 4sysops - February 20, 2013

[...] for us, Micah Rowland has spent a while reverse engineering this key, and produced a very handy script that easily allows us to force icons to be [...]

5. Eric - December 7, 2012

Nice Job! One simple problem. OpenVPN Gui Makes multiple notification entries with the same name. One is green for connected and one is red for disconnected. Your app.exe shows both entries but only allows me to change the second (red) entry. I am sure this is a quick fix in your code but I am unable to figure it out.

6. JB - September 24, 2012

I think there is a mistake in the rot13 function. The M and m are not translated to Z and z. You have to replace by this :

Function Rot13($byteToRot)
{
if($byteToRot -gt 64 -and $byteToRot -lt 91)
{
$bytRot = $($($byteToRot – 65 + 13) % 26 + 65)
return $bytRot
}
elseif($byteToRot -gt 96 -and $byteToRot -lt 123)
{
$bytRot = $($($byteToRot – 97 + 13) % 26 + 97)
return $bytRot
}
else
{
return $byteToRot
}
}

7. Jeanb - September 4, 2012

How can I use this for all users ?
I tried to customize then copy the reg to the default profile but doesn’t work.

I also tried to use it in creating an activesetup but it doesn’t like explorer kill during first logon.

For a single user, are you using it in logon script, if not, how ?

8. Script to hide system tray icons - August 9, 2012

[...] [...]

9. Greg - July 27, 2012

Is there a way to read what the program names that are stored?

10. hianz - July 13, 2012

Awesome Work Dude!

I recoded it in C#. Now its faster :) .

http://hianz.wordpress.com/2012/07/13/modify-windows-7-notification-area/

11. Brian Gonzalez - May 24, 2012

Doesn’t appear to be working for me at all. Here is my command and output. Nothing is changed on the TaskTray.
powershell -file TrayNotify.ps1 -ProgramName “WSwitch.exe” -Setting 1
Item Found with WSwitch.exe in item starting with byte 19700

It finds the item, but I can’t tell if it changes anything.

-Brian G (supportishere.com)

12. Nirav - May 3, 2012

Can somebody write this in vbscript?

13. Röndi - January 16, 2012

HI Guys

With witch Program i can find the existing Text Strings in the Binary Value. Thanks for your help.

14. Olli Janatuinen - October 18, 2011

Thank you very much about that script.

I don’t fully understand Windows logic to select name witch is used in registry but when using little bit time you can find all you want.

Here some examples:
With Microsoft Security Essentials you need use name “icrosoft Security Client” because “Microsoft Security Client” or “microsoft Security Client” not working.

Citrix Receiver Application is detected with name “Citrix Receiver”

Windows Task Manager is detected with name “Windows Task”

Windows Update is detected with name “wuauclt”

Hope this helps admins to find right program names :)

15. Robert Riegler - August 3, 2011

Hello, why does the script last so long
.\Change_W7_NotificationArea.ps1 -ProgramName “taskmgr” -Setting 1
i want to search for the Taskmanager and disable it
with above command i get following output
“Program not found. Programs are case sensitive.”
Time: 2 minutes to show output.

I´m not sure how i have to specifiy the programname
and why does it take that long?
I hav a 2core 3 Ghz computer

16. Windows 7 Notification Area Automation « MS Tech BLOG - August 3, 2011
17. Ankur Mishra - July 9, 2011

You people are genius, Greater going ….please keep it up

18. NP - July 8, 2011

Ugh. Better you than me. Thanks for figuring this out to the level you’ve done.

Oh, Microsoft… You’ve come so far with manageability, but still so much more to do…


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

%d bloggers like this: