jump to navigation

Windows 7 IE8 Favorites Bar Organization – A Descent Into Binary Registry Storage September 24, 2010

Posted by Micah Rowland in PowerShell, Windows 7.
trackback

Recently a client requested that the favorites and a different set of favorites links be populated in the favorites bar area based on the geographic location of the imaged computer. Most of the normal favorites were static, that is to say the same no matter which location the computer was at, only a few needed to be customized. In addition, the links in the Favorites Bar were required to be displayed in a specific order with any additional links placed on the far right. On the surface this appeared to be a pretty straight forward request. If only it was…

Initially I attempted to delete the contents of the %USERPROFILE%\Favorites\ folder and add a task sequence to copy a captured Favorites folder’s contents in. Unfortunately, this only partially worked. The links were required to have their respective icons cached and copying in a new set caused all of the links to revert to the default link icon. In addition, the Favorites Bar was empty and the Favorites menu had an additional folder named Favorites Bar that contained the links that should have been on the Favorites Bar itself. Time to dig in.

image

On the surface it appears that Favorites Bar links are stored at %USERPROFILE%\Favorites\Favorites Bar. Looking at the properites window, the Read-only check box is blacked out, a good indication that the folder is a “special folder”. Digging a bit deeper, I found that the “Favorites Bar” folder is a special folder whose actual folder path is %USERPROFILE%\Favorites\Links (even though it displays as “Favorites Bar”). When Internet Explorer launches, it checks for the Links folder, not the Favorites Bar folder. If the Links folder does not exist, one is created as a special folder that displays in Windows as “Favorites Bar”.

image

I’m always a fan of letting Windows do what it’s best at instead of trying to reverse engineer everything. So, to start I included the base set of favorites in the image. I then wrote a PowerShell script that determined the location based on the computers default gateway, opened the links that required customization, read through the lnk file and replace the FeedUrl and URL properties with the correct values, and rename the url files that required custom names reflecting the location. Here’s a brief script that illustrates the one used. Lets assume the link is named Link.url and the customer has 5 sites with different gateways. This script determines the gateway, maps it to a static list (you could use a environmental variable set by MDT), and then replaces the CITY in the link http://www.contoso.com/CITY/site.html and renames the favorites file.

#Get Gateway (assuming only one active network connection)
$strComputer = "."
$Nics = Get-wmiobject win32_NetworkAdapter -comp $strComputer
foreach ($Nic in $Nics)
{     $Config = Get-Wmiobject win32_NetworkAdapterConfiguration | where {$_.index -eq $Nic.index}     if ($config.IPAddress -ne $null)     {         $VGateway = $config.DefaultIPGateway     }
}
#Setup hash table and set urlLocation and city variables
$GatewayList = @{"10.0.0.1" = "urlfolder1";"10.0.1.1"  = "urlfolder2";"10.0.2.1" = "urlfolder3";"10.0.3.1"  = "urlfolder4";"10.0.4.1"    = "urlfolder5"}
$CityList = @{"10.0.0.1" = "City1, CA";"10.0.1.1"  = "City2, CO";"10.0.2.1" = "City3, WI";"10.0.3.1" = "City4, IA";"10.0.4.1" = "City5, NY"}
$urlLocation = $GatewayList[$VGateway]
$city = $CityList[$VGateway]
#Repeat for each file that needs customization
$file = get-item 'c:\users\USERNAME\favorites\links\CustomizedLinkFolder\Link.url'
$newcontents = ""
$reader = $file.opentext()
while ($reader.EndOfStream -eq $FALSE)
{     $curline = $reader.ReadLine()     if ($curline.contains("BASEURL="))     {         $newcontents += "BASEURL=<a href="http://www.contoso.com/"">http://www.contoso.com/"</a> + $urlLocation[0] + "/site.html`n"     }     elseif ($curline.contains("URL="))     {         $newcontents += "URL=<a href="http://www.contoso.com/"">http://www.contoso.com/"</a> + $urlLocation[0] + "/site.html`n"     }     else     {         $newcontents += $curline + "`n"     }
}
$reader.close()
out-file -filepath $file -InputObject $newcontents
#After all are edited, rename webslices to city
get-childItem C:\users\USERNAME\Favorites\Links\*.url | rename-item -newname { $_.name -replace 'CITYHERE', $city }

Ok, that’s not too bad, just cracking open the URL files and editing inline… So now I have the right links, on to the Favorites Bar!

Using Sysinternals Process Monitor, IE was monitored and the first time Internet Explorer opens, process monitor showed it writing to a  the contents of the Favorites Bar and stores it in the registry in a key named Order located at:

HKCU:\Software\Microsoft\Windows\Currentversion\Explorer\Menuorder\Favorites\Links

So let’s open up the registry and take a look…

image

image

Oh boy!

A half hour later and a few well crafted web searches later (A needle in a haystack search… got lucky) landed me at a website that described the format of this key for Windows XP and Windows Vista.

http://www.codeproject.com/KB/shell/iefavdecon.aspx

After comparing the binary format as described at this site with the stored values, something didn’t quite line up so I loaded the hex into excel and started reverse engineering the data using the provided binary format information as a starting point.

image

The information from Code Project was invaluable at deconstructing this (and I learned a lot about how Microsoft stores data in binary registry values). The Subrecord1 header in Windows XP is 18 bytes long, in Windows Vista is 36 bytes long, and, as I found, in Windows 7 is now 40 bytes long. There may be other changes to each field, but the main change is the size of that header. A side point: the length field of the header is stored in 2 bytes which as you read the hex need to be reversed…. so a length of 1C0 (448 bytes) is stored as C0 01.

Code Project does a  good job of describing the format, once you understand it… I’ll try to briefly explain and diagram it, I hope it clears it up a bit.

The data is stored much like a TCP IP packet with encapsulation (stay with me here). Think of it as a giant envelope that has a bunch of envelopes inside, and each envelope inside has an envelope in it that contains a letter. The binary data starts with a header that is 20 bytes long. This header tells us how many bytes long the payload is and how many items are in it. After we read the header, the first payload (envelope) follows. This envelope has an 8-byte header as well that tells you how long (how many bytes) its payload is as well as *TADA* the order it is to appear in the Favorites Bar. The length is how we know where one favorites bar item ends and the next begins. We then grab that many bytes and we have the item envelope. The item envelope has a 28-byte header that tells us what type of item it is (link or folder). Finally we have the “letter”, the actual data about the favorites bar item. The data contains such information as the file name of the item, the type of the item (link or folder)

image

After rereading that explanation above, I’ll be honest, it’s a complex topic and trying to explain it may have just made some readers’ nose bleed.

Sufficed to say, the above information allowed me to craft a PowerShell script that reads the data, scans each item and checks it’s longname property for a specific set of strings, and sets the order value in the purple header as desired.

And here’s the PowerShell script to reorder:

$key = get-item HKCU:\software\microsoft\windows\currentversion\explorer\menuorder\Favorites\Links
$value = $key.getvalue("Order")
write-host Original Size: $value.count
[byte[]] $newvalue = @()
$newvalue = $value[0..19]
$payload =  $value[20..$value.GetUpperBound(0)]
$list = @{}
[int] $i = 0
$remPayload = $payload.Clone()
write-host Payload Size: $remPayload.Count
while ($remPayload.Count -gt 8)
{     [byte[]] $currentrecord = @()     $length = $remPayload[3]*65536+$remPayload[2]*4096+$remPayload[1]*256+$remPayload[0]     $currentrecord = $remPayload[0..($length-1)]     write-host Record $i Size = $length     $list.add($i, $currentrecord)     $remPayload = $remPayload[$length..$remPayload.GetUpperBound(0)]     $i++
}
#$list.add($i.tostring(), $remPayload)
$order = 6
write-host Begining For list is $list.count members
for ($i = 0; $i -lt $list.count; $i++)
{     write-host Processing list item $i order $list[$i][4]     if ($list[$i].count -gt 8)     {          $startposition = $list[$i][-8] + 49          $teststring = ""          for ($y=1; $y -le 14; $y++)          {             $teststring += [char] $list[$i][$startposition + $y]          }          #$teststring          $n = [char](0)          if ($teststring.Contains("D"+$n+"o"+$n+"w"))          {             $list[$i][4]=0          }          elseif ($teststring.Contains("W"+$n+"e"+$n+"a"+$n+"t"+$n+"h"+$n+"e"+$n+"r"))          {             $list[$i][4]=1          }          elseif ($teststring.Contains("T"+$n+"r"+$n+"a"+$n+"f"+$n+"f"+$n+"i"+$n+"c"))          {             $list[$i][4]=2          }          elseif ($teststring.Contains("N"+$n+"e"+$n+"w"+$n+"s"))          {             $list[$i][4]=3          }          elseif ($teststring.Contains("W"+$n+"e"+$n+"b"))          {             $list[$i][4]=4          }          elseif ($teststring.Contains("C"+$n+"o"+$n+"n"+$n+"t"+$n+"o"+$n+"s"+$n+"o"))          {             $list[$i][4]=5          }          else          {             $list[$i][4]=$order             $order++          }     }
}    for ($i = 0; $i -lt $list.count; $i++)
{     if ($list[$i].count -gt 8)     {         write-host List item $i byte 10 = $list[$i][10] order = $list[$i][4]             $newvalue += $list[$i]     }
}        $value.count
$newvalue.count
set-itemproperty HKCU:\software\microsoft\windows\currentversion\explorer\menuorder\Favorites\Links Order $newvalue
write-host Total List Items: $list.count

As you can see, the script is very static, it’s looking for a link that has Dow, Weather, News, Web, or Contoso and puts those in positions 0 through 5, any other links are put afterwards.

Theoretically this script could be executed as part of a logon script so that the favorites bar gets reorganized everytime the user logs on and places any user added links at the end of the favorites bar.

Well that’s it. Wish this was stored in a much easier format like XML. C’est la vie.

Advertisements

Comments»

1. Windows 7 IE8 Favorites Bar Organization « MS Tech BLOG - August 3, 2011
2. Windows 7 Notification Area Automation – Falling Back Down the Binary Registry Rabbit Hole « Xtreme Deployment - July 8, 2011

[…] 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 […]


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 )

Google+ photo

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

Connecting to %s

%d bloggers like this: