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.
comments closed

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!

Advertisements

New Tool: Advanced Format Drives April 29, 2011

Posted by keithga in Troubleshooting, Uncategorized, Windows 7.
3 comments

Been slacking lately (Working on MDT 2012).

Advanced format drives are coming!

http://en.wikipedia.org/wiki/Advanced_Format

This hasn’t been much of a problem until recently, now that most of the major hard drive manufacturers have started to switch over to full Advanced Format.

When you have an Advanced format drive, you should format with an operating system that can format the drive aligned on the advanced 4k sectors rather than the 512 byte sectors found on older drives.

If  you do not format an “Advanced Format” disk aligned on the 4k sectors, then you may experience performance degradation. Windows 7 SP1 has been updated to address Advanced Format Drives, and WinPE 3.1, contained in WAIK 3.1 has been updated as well.

But how do you know if you need WinPE 3.1?

I have just written a new tool: IsAdvancedFormat.exe, that can detect if there are *any* advanced format drives present on the local machine. It will return errorlevel 0 if there are no advanced format drives, and errorlevel 42 if there *are* advanced format drives.

Source code is included.

Sample Output!

C:\> IsAdvancedFormat.exe
Enumerate all detect all Advanced Format Drives.
Enumerator: IDE Found: Maxtor 6L160M0
  Physical sector size is 512 bytes.
Enumerator: IDE Found: Maxtor 6L160M0
  Physical sector size is 512 bytes.
Enumerator: IDE Found: WDC WD10EADS-11M2B2
  Emulated sector size is 512 bytes.
  Physical sector size is 4096 bytes.
C:\> @echo %ErrorLevel%
42

Location!

http://cid-5407b03614346a99.office.live.com/self.aspx/Blog/IsAdvancedFormat.zip

Update! (7/24/2011)

It appears that Dell has taken this tool and enhanced it! Their new tool can detect if an Advanced Format drive was partitioned/formatted incorrectly.  Cool Stuff.

Since Dell’s tool is now a superset of my tool, I would recomend theirs first:  http://del.ly/afhdd 

I will continue to provide my tool here as a reference.

Thanks Dell! (and thanks Warren Byle)

MDT Litetouch startup -Super Flow- help; November 11, 2010

Posted by keithga in MDT 2010, Troubleshooting, Windows 7.
3 comments

Someone posted a question on a MDT forum recently:

I´m courious about what is happening when you boot your client on the WinPE iso image that is generated when you update the deploymentshare in MDT.

So, does a “superflow” or other documentation like that exist ?

Interesting question, thought I’d write down some of the basics (Things that are interesting to people in the deployment field):

  •  The Computer starts up and the BIOS is responsible for selecting which device to boot from. This can be either:
    Network (PXE), Hard Disk, USB Flash Drive, or CD-ROM (El-Torito)
  • When booting off a hard disk or USB Flash Drive, the BIOS will look for an “Active” partition, and run the code in the Master Boot Record.
  • PXE Booting is a different topic…  :^)
  • When booting from the CD-ROM, the BIOS will kick off the EtfsBoot.com program running in El-Torito No-emulation mode. ETFSBoot.com does two things:
    • Runs the BootFix.bin program – This program will test for the existence of any “Active” hard disk partitions on the machine, if found, it will prompt the user to “Press Any Key to boot from CD or DVD.” If no “Active” partition is found, or the user presses a key, then the next “BootMgr” program is run.
    • Bootmgr – ( In windows XP it was ntldr) Is responsible for management of the other Operating Systems, it reads the \boot\bcd file (a registry hive), and displays a menu to the user. It can launch Windows, WinPE, other Real Mode programs, and even boot Windows from VHD files (for premium versions of windows like Windows Ultimate or Enterprise).
  • Once the OS has been selected, BootMgr then starts the process of loading all the OS components into memory, when ready it will pass control from Real Mode into the Windows Kernel. The OS will then take over the boot up process and continue loading the rest of the drivers and components. If at this point it can’t find the hard disk from where it came from, it will Stop BugCheckEx(), with error code 0x0000007B, typically this means the Storage Driver wasn’t loaded.
  • In WinPE the process continues by calling WinPEShl.exe. If WinPEShl.exe finds the file WinPEShl.ini, it will parse it. For MDT 2010, the WinPEShl.ini file looks like:
[LaunchApps]
%SYSTEMROOT%\System32\bddrun.exe,/bootstrap
  • BddRun.exe – Will launch wpeinit.exe. It will also remain in memory and monitor the keyboard for F8 (See: http://deployment.xtremeconsulting.com/2009/10/29/78/ )
  • WpeInit.exe will start up the network, and other services and parse the unattend.xml file for commands to run. For MDT 2010, the Unattend.xml file looks like:
<?xml version="1.0" encoding="utf-8"?>
…
<Path>wscript.exe X:\Deploy\Scripts\LiteTouch.wsf</Path>
  • Litetouch.wsf will then check to see if there are any Task Sequences in progress (is there a c:\minint and/or c:\_SMSTaskSequence\TSEnv.dat file present), and if so execute.
  • If Litetouch can’t find any in-progress Task Sequences (NewComputer), it will:
    • Parse the Bootstrap.ini file
    • Display the Welcome Wizard (unless SkipBDDWelcome is defined)
    • Parse the CustomSettings.ini file (Typically from the DeployRoot found in the Bootstrap.ini file).
    • Display the Deployment Wizard (Unless SkipWIzard is defined)
    • Run the full Task Sequence…

<whew>

P2V Migration for Software Assurance Beta 2 Now Available – with System Center Configuration Manager 2007 integration September 26, 2010

Posted by keithga in Announcements, MDT 2010, System Center Configuration Manager, Troubleshooting, USMT, VBscript, Windows 7.
comments closed

 

We’ve been busy here at Xtreme Consulting Group, recently Keith worked as a Developer on the P2V Migration project with Microsoft.

For more information on P2V Migration, click here.

P2V Migration adds documentation and support of System Center Configuration Manager 2007 Zero Touch Installation! 

What is better than spending a moment to kick off a completely automated process to redeliver an existing operating system as a virtual machine within a new build of Windows 7?
Answer: Making the entire process “zero touch” without necessitating a visit to the target computer or manually initiating the migration!

P2V Migration for Software Assurance can now be implemented using System Center Configuration Manager 2007 Operating System Deployment as well as native Lite Touch Installation with the Microsoft Deployment Toolkit 2010 U1. Computer refresh, replace and restore task sequence templates for Configuration Manager are included and documented in this Beta release.

clip_image001 P2V Migration templates integrated with Microsoft Deployment Task Sequence options in System Center Configuration Manager. Can be created and advertised as with other task sequence options.

Additional optimizations beyond Configuration Manager functionality included in this release are:

1. Better flexibility for backing-up and restoring VHD files using default file locations

2. Support for PCs using system and boot volumes

3. Globalization of scripts to handle varying regional and locale formats

4. General bug fixes and improved documentation

These fixes reflect the feedback of our Connect community and MVPs – thanks to everyone for submitting feedback!

Download P2V Migration for Software Assurance Beta 2 now:

P2V Migration for Software Assurance

New to P2V Migration for Software Assurance?

 

This solution was built to help unblock OS deployments by redelivering blocking users’ old Windows environments, applications and browsers seamlessly in Windows 7 using automated physical-to-virtual migration

P2V Migration for Software Assurance uses the Microsoft Deployment Toolkit, Sysinternals Disk2VHD and optionally System Center Configuration Manager 2007 to convert a user’s existing Windows XP or newer client environment to a virtual hard disk then automates the delivery of an updated and personalized Windows 7 operating system containing a virtual machine with the user’s previous Windows environment, applications and Web browser. The user’s previous virtual desktop retains its existing management components, domain membership and policies. The process also publishes applications and the browser for the user to access them seamlessly within Windows 7’s start menu.

How it Works

clip_image002 Completely automated process enables the previous operating system to be a child virtual machine inside the Windows 7 host.
clip_image003 Standalone application and Internet Explorer links published from virtual machine to native Windows 7 start menu. These applications can be launched individually using RemoteApp integration – without showing the entire virtual machine’s desktop.

Keith

Keith Garner is a Deployment Specialist with Xtreme Consulting Group

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

Posted by Micah Rowland in PowerShell, Windows 7.
2 comments

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.

UserTile Automation June 23, 2010

Posted by Micah Rowland in USMT, VBscript, Windows 7.
17 comments

Recently a customer requested that two accounts be created during the MDT process and required each to have a preset user account picture (referred to by Windows as the User Tile). There are a number of ways to accomplish this, USMT/Easy Transfer wizard being the easiest. However, due to the processes they already had built into the base image, I decided the best tack was to investigate the actual process employed in storing the User Tile and build automation that could quickly accomodate changes to the specifications of which image to use. After a quick search of the web, it seems that this particular problem, programatically changing the User Tile has not yet found a solution. I present to you my findings along with a sample script to tackle this seemingly simple manual process.

The first step in automating any process is to do it manually first and watch what happens. I rely on the Sysinternals tool Process Monitor as well as Process Explorer for most of my research. Launching Process Explorer as an administrator led me down a few dead ends. It was only after running Process Explorer in the System context using PSExec with the -s and -i switches that I was able to locate the location that Windows 7 uses to store the user tile.

The User Tiles configuration information is stored in Windows registry at HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\########, where ######## is a unique 8 digit hexadecimal ID, in as a binary value named UserTile. But how can we cross reference a username to this 8-digit ID? Glad you asked!

Taking a further look at the registry key, you’ll notice that beneath the user ids, there is another key called Names. If you expand this key you’ll see a list of all local user accounts on the system. Upon opening any of the user keys you’ll notices that only a default value exists. However, take a look at the value type… It is not a standard type, but a hexidecimal number 0x### where the ### id crossreferences nicely with the list of user ids above. In our script we need only pad this value with leading 0’s until it reaches the desired 8-digits. Trying to retrieve this hexidecimal “type” throws exceptions in nearly every method I tried. Exporting the key to a .reg file gave me the output I needed to be able to search for a specific username and retrieve its id.

The SAM Hive of the registry is not generally accessible to any user accounts and is configured, by default, to only be accessed by the System account. To maintain security, both the read and the write operations necessary to modify the UserTiles programmatically are accomplished by executing the operations in the System context. Execution in the system context is accomplished by utilizing the PSExec tool.

When a user interactively changes the UserTile by means of the User Account control panel, Windows resizes the image specified by the user to 128×128 and saves new image as a 24-bit bitmap file in the C:\ProgramData\Microsoft\User Account Pictures\ folder as USERNAME.bmp. The bitmap is then stored in the registry in the SAM and the user’s contact card is updated.

The binary data is composed of a header followed by a payload containing the binary graphic used for the UserTile and closed with a footer that contains the path to the file used as the UserTile. The header is 16 bytes long.

  1. 12-bytes (seem to be constant at 01 00 00 00 03 00 00 00 01 00 00 00)
  2. 4-byte field representing the size of the payload

The payload data reveals that the image stored in the registry is 126×126 pixels, presumably to make up for the 1 pixel wide border around the image when displayed on the logon screen and on the start menu. Further, the image is stored in 16-bit color depth using BI_BITFIELDS compression.

The footer contains the type of image file used and the location of the file used using Unicode (2-bytes per character). The format is as follows:

  1. 4-byte field (purpose unknown, possibly the length of the following field)
  2. A null-terminated Unicode string representing the file type
    1. Eg. “bmp” = 62 00 6D 00 70 00 00 00
  3. 4-byte field (purpose unknown, always 02 00 00 00)
  4. 4-byte field representing the payload (bitmap) size in bytes
  5. A null-terminated Unicode string representing the file location padded to the nearest 4 bytes.

Phew! Needless to say, this level of detail is not necessary for part 1 of this post, however in crafting a truely dynamic programmatic approach to changing the uer tile, we will need this information.

Solution:

It should be possible to engineer an application that accepts a username and a bitmap file to use. However, for part 1, it is simpler to export the registry keys from a sample machine using either the reg.exe or the regedit.exe utility under the System context using PSExec.

When scripting this automation, we must be aware that the first time PSExec is run, a EULA must be accepted. To avoid this, the following registry file is imported (AcceptPSExecEULA.reg):

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Sysinternals\PsExec]
"EulaAccepted"=dword:00000001

The following script was created to accomplish the goal of changing the UserTiles:

''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'   Title:   User Tile Change Script
'   Author:  Micah Rowland (Xtreme Consulting)
'   Date:    07/14/2010
'   Desc:    This script is designed to programatically replace
'            a single local user's User Tile on Windows Vista
'            and above.
'   Prereq:  This script requires the use of PSExec available
'            from http://live.sysinternals.com/psexec.exe
'   Usage:   UserTile.vbs USERNAME USERTILEFILE
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
CONST FORREADING = 1
CONST FORWRITING = 2

set objShell = CreateObject("Wscript.Shell")
set objFSO = CreateObject("Scripting.FileSystemObject")
set objArgs = Wscript.Arguments
set objShell = CreateObject("Wscript.shell")
if CheckArgs() <> "" then
     Wscript.echo Checkargs() & vbcrlf
     wscript.echo "Arguments Invalid. Usage: ChangeUserPicture.vbs USERNAME UserTileFile"
     wscript.quit()
end if

strUsername = objArgs(0)
strUserTileFile = objArgs(1)
strUserIndex = GetUserIndex()
set objFile = objFSO.GetFile(strUserTileFile)
set objTS = objFile.OpenAsTextStream(1)
strUserTile = objTS.ReadAll
wscript.echo strUserTile
strRegFile = objShell.ExpandEnvironmentStrings("%temp%") & "\UserIndexes2.reg"
set objRegFile = objFSO.OpenTextFile(strRegFile, ForWriting, true)
contents = "Windows Registry Editor Version 5.00" & vbcrlf & vbcrlf & "[HKEY_LOCAL_MACHINE\SAM\SAM\Domains\Account\Users\" & strUserIndex & "]" & vbcrlf & strUserTile
objRegFile.Write contents
objRegFile.Close

strImport = "PSEXEC -S -I reg import " & strRegFile
objShell.Run strImport

Function GetUserIndex

     ' This function exports the Names key of the SAM from the registry to determine a match between the user account provided
     ' and its hex ID for use in creating the new registry file for import. Because the .reg file is exported in Unicode format
     ' we use the type command to pipe it from stdout to a new text file.

     strUsername = objArgs(0)
     strUserTileFile = objArgs(1)
     objShell.run "cmd /c reg export HKLM\SAM\SAM\Domains\Account\Users\Names %temp%\UserIndexes.reg /y"
     wscript.sleep(500)
     objShell.run "CMD /C type %temp%\userindexes.reg > %temp%\userindexes.txt"
     wscript.sleep(500)
     set objFile = objFSO.GetFile(objShell.ExpandEnvironmentStrings("%temp%") & "\userindexes.txt")
     set objRegExport = objFile.OpenAsTextStream(FORREADING)
     curLine = objRegExport.ReadLine()
     do until instr(lcase(curLine), lcase(strUserName)) or objRegExport.atendofStream
          curLine = objRegExport.ReadLine
     loop
     if objRegExport.AtEndOfStream then
          wscript.echo "Username not found."
          wscript.quit()
     else
          curLine = ObjRegExport.ReadLine
          tmpGetUserIndex = mid(curLine, instr(curLine, "(") + 1, len(curline) - instr(curline, "(") - (len(curLine) - instr(curline, ")")+1))
          do until len(tmpGetUserIndex) = 8
               tmpGetUserIndex = "0" & tmpGetUserIndex
          loop
          GetUserIndex = tmpGetUserIndex
     end if
End Function

Function CheckArgs()
     ' This function makes sure that 2 arguments were provided and that the filename provided exists
     if objArgs.Count <> 2 then
          CheckArgs = "Exactly 2 arguments must be specified."
     elseif not objFSO.FileExists(objArgs(1)) then
          CheckArgs = "File not found."
     else
          CheckArgs = ""
     end if
 End Function

The script syntax is: SetUserTile.vbs USERNAME FILE

The file used by this script consists of only the UserTile value from a registry export of a preconfigured UserTile. This can be accomplished by setting a local user account’s picture, opening regedit using the psexec -s -i regedit.exe command, navigating to the SAM key mentioned above, determining the correct user account as detailed above, and exporting the key. Then remove all data in the .reg file except the “UserTile=hex:…” entry.

To leverage this script, a command-script file was created and added to the Software Installation task sequence.

reg import AcceptPSExecEULA.reg
cscript SetUserTile.vbs "USERNAME" DATAFILE.txt

I hope to have a commandline based program developed in the future to tackle this which will accept any sized bitmap image as it’s input as opposed to using a captured registry value. I hope you have enjoyed this post. If you have any questions feel free to ask!

Great Overview on Windows Deployment A-Z… January 18, 2010

Posted by keithga in Announcements, MDT 2010, Windows 7.
add a comment

Our friend Jeremy Chapman has written a whitepaper:

Deploying Windows 7 from A to Z.doc
http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=dfafb346-97dd-4fca-947e-3d9149834da6

It’s a good introductory reference to MDT, SCCM, and Deployment in general.

Keith

Keith Garner is a Deployment Specialist with Xtreme Consulting Group

Missing XML files when using USMT and ConfigMgr SP2 November 25, 2009

Posted by tmintner in System Center Configuration Manager, USMT, Windows 7.
Tags: , , ,
add a comment

So you just upgraded all of your ConfigMgr 2007 Site Servers to Sp2 and you think you are all ready to deploy Windows 7.  You integrate the latest MDT 2010 integration pieces and walk through the wizard to create your task sequence and create your new USMT 4.0 package.  You then decide to deploy Windows 7 to one of your existing Windows XP clients and it should migrate all of the user data and install the new OS…right?  Well after the installation starts it quickly finishes and doesn’t install Windows 7.  After looking through the smsts.log file, you will notice that the task sequence failed on the Capture User State Step with an error message that it cannot find miguser.xml.  Closer examination shows that it is looking for the XML file at the root of the package.  Looking at the contents of the USMT package, you will notice that the XML files are in the X86 folder or X64 folder.  Easy enough to fix, those MDT guys must have pointed the package to the wrong directory.  So you modify the package to point to the X86 folder and try it again.  Does it work?  NO!!  This time it fails because it cannot find scanstate.exe.  So what in the world is wrong?

Configuration Manager SP2 updates all of the executables that ConfigMgr uses for an OS Deployment.  One of the files that is used during a USMT migration is called OSDMigrateUserState.exe.  During an OS Deployment this file will be in one of two places.  For a New Computer deployment the file will be on Windows PE.  If you are doing a Refresh from an existing operating system the file will reside in the client’s local CCM directory.  If you just upgraded your site servers to SP2 the client machines won’t have the upgrade OSDMigrateUserState executable.  Prior to SP2, that executable had no knowledge of USMT 4 and the directory structure change of USMT.  So if you want to use USMT 4 in your ConfigMgr deployments you are going to need to upgrade all of your clients to ConfigMgr Sp2 as well!

This is just one example where mismatched SP1 and SP2 versions on your client machines will cause issues with Windows 7 deployments.  As a best practice always make sure you have SP2 fully deployed to all of your site systems AND all of your client computers before starting a Windows 7 deployment.

 

-Tim Mintner

Understanding USMT with MDT 2010 November 20, 2009

Posted by tmintner in MDT 2010, USMT, Windows 7.
Tags: , , , ,
21 comments

The User State Migration Tool (USMT) is an extremely powerful and essential piece of an Operating System Deployment.  With the release of the Windows AIK for Windows 7, USMT 4.0 was made available and includes many great new features such as hard-link migration (for more information on what’s new in USMT 4.0 see this article).  MDT 2010 integrates USMT directly into the task sequence for both Lite Touch Installations and Zero Touch Installations.  There have been several questions since MDT 2010 was released on how USMT works with MDT and how all of the pieces fit together. 

 

Lite Touch Installation

USMT 4.0

In order to user MDT 2010, you must install the Windows AIK for Windows 7.  The Windows AIK now includes USMT 4.0 as a feature of the AIK so there is no longer a separate download required for USMT.  When you create your Deployment Share inside of the Deployment Workbench, MDT will create a folder at the root of the deployment share called USMT and copy the USMT files into that folder. 

 

Refresh Scenario

A refresh scenario means that you are deploying an Operating System to a computer that already has an existing Operating System using a task sequence created with the Standard Client Task Sequence.  The process is started from within the current Operating System.  In a Refresh scenario, the disk is not formatted or partitioned so in most cases, the user data can be saved locally on disk saving time and network bandwidth.  During a Lite Touch Installation there are only two steps in the task sequence step that uses USMT.  The first step is called Capture User State and the second step is called Restore User State.  This step will run a script called ztiuserstate.wsf.  The actions that ztiuserstate will be performed are determined by the following factors:

  • The Operating System being deployed
  • The values provided in CustomSettings.ini for UserDataLocation and UDDIR and UDShare
  • The values provided in the Client Deployment Wizard used to start the Task Sequence

If ZTIUserState determines that you are deploying Windows XP it will not be able to use USMT 4.0 and will try to use USMT 3 for the user state migration (more on that later).  If you have specified that UserDataLocation = Network and you have also specified a UDDIR and UDShare in the customsettings.ini then USMT will migrate all of the files to a compressed file at the network location you specify.  Also if you provide a network location in the Client Deployment Wizard, USMT will migrate the user data to a compressed file at the network location you specify.  If UserDataLocation = AUTO and you are not deploying Windows XP, the ztiuserstate script will keep the data local in the MININT folder using hard-link migration.  There is no need to do an estimate because the hard-link migration process only requires 250 MB of space on the disk and if you didn’t have that much the process would fail in the validate step.

Replace Scenario

A Replace scenario is made up of two task sequences.  The first task sequence is based upon the Standard Client Replace template and the second task sequence is based on the Standard Client task sequence template.  The first task sequence is initiated from within the existing Operating System like in a Refresh scenario.  The USMT capture process in a Replace scenario works just like a Refresh scenario with one exception, in a Replace scenario the user data can not be stored locally so you must provide a network location to store the User Data.

The second task sequence is started by booting into the Lite Touch boot image and doing a New Computer deployment.  The Client Deployment Wizard will ask if you want to restore user state and where the user state is stored.  The Restore User State step in the task sequence would then use USMT to restore the user state to the computer being deployed.

 

USMT 3.01

So if USMT 4 has all these new cool features, why would you ever still want to use USMT 3?  Well USMT 4.0 cannot restore user data onto Windows XP.  Loadstate.exe from USMT 4.0 will not run on Windows XP.  So if you plan on migrating user data to Windows XP you will still need to use USMT 3.01.  To use USMT 3.01 you will need to download it from Microsoft.  The Deployment Workbench has direct links to both the 32 bit and 64 bit versions of USMT 3.01 in the Components section.  After downloading the installation files you will need to place those installations files in the Tools\X86 folder in your Deployment Share for 32 bit installs and Tools\X64 for 64 bit installs.

Refresh Scenario

In a Refresh scenario,the Lite Touch Installation process will be able to determine if the Operating System being deployed is Windows XP.  The same factors mentioned above are applicable for USMT 3.01.  If the Operating System is Windows XP, the ztiuserstate script will install USMT 3.01 on the computer and then do an estimate to determine how much user data is on the computer.  If the amount of user data is less than the amount of disk space needed then ztiuserstate will store the user data on the local disk in the MININT folder.  If there is not enough space then the user data will be stored in the UDSHARE and UDDIR locations.

Replace Scenario

A Replace scenario is a little trickier.  The Lite Touch Installation process won’t be able to automatically determine what Operating System you are going to be deploying because the Operating System is going to be deployed with a separate task sequence potentially even on a different computer.  In a Replace scenario ztiuserstate will use USMT 4.0 by default, however there is one problem with that.  User Data backed up with USMT 4 cannot be restored with USMT 3 and USMT 4 cannot be run on Windows XP!  So if you are replacing a Windows XP computer with another Windows XP computer, how can you make sure that USMT 3.01 is used?  To handle that scenario, MDT has a variable called USMT3.  To force the Replace scenario to use USMT 3 you would enter the following into your customsettings.ini:

USMT3 = YES

 

Zero Touch Installation (ConfigMgr/SCCM)

The USMT process with ZTI is slightly different than the process with Lite Touch.  The steps in a ZTI task sequence look like the following for capturing User State:

image

The MDT integrated task sequence uses the built in actions for Request State Store, Capture User State, Release State Store, and Restore User State.  The Ztiuserstate.wsf script is only used in one step: Determine Local or Remote User State. 

With ZTI, the version of USMT that is used is solely dependent on the USMT package that is associated with the Determine Local or Remote User State, Capture User State, and Restore User State steps in the task sequence.  You will need to create a package that contains the files for either USMT 3.01 or USMT 4.0.  Thankfully when you walk through the wizard to create the MDT task sequence in Configuration Manager, the MDT provided wizard will create the USMT 4.0 package for you automatically.  If you want to create a USMT 3.01 package you will need to download and install USMT 3.01 on a client computer and use the installation folder in the Program Files folder as the source directory for your package.

USMT 4.0

The version of USMT that you are using for your USMT package will determine what actions the Determine Local or Remote User State will perform.  If you are using USMT 4.0 then the Determine Local or Remote User State will set the variable needed to do a hard-link migration and also set a variable called USMTLOCAL =TRUE.  The Request State Store and Release State Store steps in the task sequence have a condition on them so that they will only run if USMTLOCAL is not true.  That means if you are using USMT 4, the user state will always be local and the State Migration Point will never be used.  If you would like to use the State Migration Point instead of using hard-link migration then just disable the Determine Local or Remote UserState step in the task sequence

USMT 3.01

If you are using USMT 3.01 then the Determine Local or Remote User State step will perform a USMT Estimate to determine if there is enough space locally on the disk to store the user data.  If there is enough space locally the variable USMTLOCAL is set to TRUE.  Again the Request and Release State Store steps are conditional on this variable so if there is not enough room locally the State Migration Point will be used

 

Summary

Whew..that is a lot of information.  As you can see, MDT tries to cover all of the scenarios with USMT whether you are using USMT 4.0 or still deploying Windows XP and need USMT 3.01.  Enjoy and happy deploying!

 

-Tim Mintner

New Tool: MDT 2010 Build-out/Hydration Evaluation November 13, 2009

Posted by keithga in MDT 2010, PowerShell, VBscript, Windows 7.
comments closed

I wanted to give a glimpse of some projects we are working here at Xtreme Consulting Services – Deployment Team.

This is a Microsoft Deployment Toolkit 2010 Build-out (sometimes called Hydration) system.

It starts off as a small 3MB *.zip file. The MDTBuildout.exe program will automatically download all required files (around 10GB of files), create a Virtual Machine running Windows Server 2008, and build out a functional MDT 2010 system. All the files downloaded are publicly avaiable over the internet.

This sample is *not* designed for production environments. The Server OS and Windows 7 Client OS’es downloaded are time-limited Evaluation versions of Microsoft OS’es, and not intended for production environments. If you would like to setup a functional system for your corporate environment, please let us know.

Xtreme Consulting Service’s Deployment Team is ready to assist you with your deployment needs.

Features

When complete, this Buildout (Hydration) system will create:

  • A Virtual Machine running Windows Server 2008 (R1) x86 Server Standard.
  • MDT 2010, with all dependencies installed (including WAIK).
    • OS’es: Server 2008 R1 x86, Windows 7 x86 and Windows 7 x64.
    • Microsoft Office 2007 Enterprise Evaluataion Version.
    • Applications: 7-zip, Foxit PDF Reader, MacroMedia player, Silverlight player, SQL Server 2008 Express, Sun Java Runtime, Video Lan Player, Windows Debugger (windbg).
    • Sample Driver package: Intel Chipset drivers.
    • Sample Packages: Windows 7 Language Packs, Hyper-V Drivers, Remote Server Administration Tools (RSAT)
  • A Windows 7 Media build DVD image with Office 2007.
  • It will also enable a PXE/WDS Service for servicing MDT 2010 WinPE Images (no Domain Controller required).
  • A blank Virtual Machine ready to perform a capture of Windows 7 back to the MDT 2010 server.

Requirements

  • Your machine must be running windows with a version of either Hyper-V (for Windows Server 2008) or Virtual PC 7 (for Windows 7).
  • Your machine must have at least 2GB of ram, and 60GB of free hard disk space.

Walkthrough

Download the MDTBuildout.zip file to your local machine and expand the contents to their own folder. You should see the manual: MDTBuildout.rtf and the Wizard Wrapper MDTBuildout.exe. Go ahead and launch the MDTBuildout.exe.

The MDTBuildout.exe wizard will guide you through the process of creating the virtual machine and downloading the necessary files to get it started. The wizard will create to Virtual Machines:

MDTVirtualServer – This is the Virtual Server that will host MDT 2010. It takes a couple of hours to setup, depending on the speed of your network connection.

image

MDTVirtualClient – This Virtual Machine will be used later on to capture a reference image of Windows 7 with Office 2007.

image

It is recommended that you use the default settings during the MDTBuildout.exe wizard, although, you can override the settings if required. Be sure to give the Virtual Machines enough memory, place the *.VHD files on a disk with 40+GB of space free, and select the Virtual Networking adapter with internet access.

When you select “Build” the wizard will begin the process of downloading the Windows Server 2008 Eval *.iso file to the local machine, this file is about 1.8GB. When done, the wizard will automatically create the virtual machines with the required parameters, and start the process. You can continue to watch the progress in the VM window.

The entire build process can take several hours depending on the speed of your network connection. Let the install run. When complete, you should see a dialog showing the status.

The password for the MDT Server is: P@ssword

When complete, you can start the MDT 2010 Workbench (start –> Microsoft Deployment Toolkit –> Deployment Workbench), and view the components added to the system.

image

The deployment share should be at: c:\deploymentshare

The media share should be at: c:\media

If you wish, you can start the PXE server, to allow other computers on the network to boot into MDT 2010, this is a great way to upgrade machines on your corporate network in a “self-service” fashion. To enable the pxe server, run the command “net start wdsserver”. WARNING: The PXE/WDS Server running on the Virtual Machine *will* conflict with other PXE/WDS servers on the network. Please ensure that you are the *only* PXE/WDS server on the network before starting! This is why the WDS service is disabled by default.

You can also burn the contents of the Win7 Media ISO to a DVD drive. Or perhaps copy the *.iso file to the local machine.

Use this server to evaluate MDT 2010 and try deploying Windows 7 (Evaluation Version), to your test lab.

When ready, you can boot the MDTVirtualClient Virtual Machine, using either the Win7 Media ISO image, or PXE/WDS. The Client Virtual Machine has been specially configured to auto install Windows 7 and upload the captured image back to the MDT 2010 Server in a completely hands off fashion.

More documentation, walkthroughs, and videos to follow.

Link

<Edit> No longer available.

Keith

Keith Garner is a Deployment Specialist with Xtreme Consulting Group