lørdag 3. desember 2016

Unpin Edge with pure PowerShell, take two

Earlier this year I wrote a PowerShell script to unpin Microsoft Edge from the taskbar, since Microsoft didn't offer any official way to do so. Since then they have added a way to do it through Group Policy, but unfortunately this method seems a little half-baked. I stuck to using my script.

While the original script worked, it used a very stiff and fragile method to locate Edge in the taskbar registry key. If the Edge version number changed, the script would have to be updated. I've written a new version of the script which is intended to be less picky about new Edge versions. As a bonus, it comes with support for removing the Windows Store app right out of the box. If you don't want to unpin Windows Store you can just remove the entry from the hash table.

Note that changing the taskbar in this manner is not officially supported by Microsoft, and if something goes wrong it will likely end in all apps and applications being unpinned from the taskbar. Use the script at your own risk and make sure to thoroughly test it in your specific environment before using it in a production setting.

Here's the script:
Unpin Edge (and Windows Store)

The original version of the script can be found here, along with some more details on how it works.

fredag 17. juni 2016

Unpin Edge (and pin Internet Explorer) with pure PowerShell


The tl;dr
Here are the scripts, use at your own risk:

The first two scripts have some hopefully sensible comments, but I couldn't be bothered adding comments to the last one.

Update 2017-05-01
Added a link to the newer version of the script, which has been confirmed to work under Windows 10 version 1703 (Creators Update).

Update 2016-08-27
The script has been updated with the byte array for Edge on Windows 10 version 1607 (or Anniversary Update). Please note that the byte array was dumped on OS build 14393.82, since this was the newest available at the time. I had one computer that was running an older build of 1607, and I noticed that the byte array was different even if the Edge version number was the same as on the computers with build 14393.82.

If the script does not work for you it is probably because the Edge byte array on your particular flavor of Windows is different from the ones I have included in the script. To get around this you need to:

  1. Get the byte array for the app you want to remove
  2. Find the indexes of the "random" bytes in the array
  3. Add these two pieces of information to the UnpinApps.ps1 script
Get the byte array
This must be done once on two different computers (VM or physical doesn't matter). The computers need to be running the exact same edition of Windows and have the exact same updates installed. Copy the GetAppByteArray.ps1 script to each of the computers, unpin (yes, unpin) the app you're targeting from the taskbar, run the
GetAppByteArray.ps1 script, pin the app and finally click the script window and press a random button to unpause it. This will dump the byte array as a comma-separated list of decimals to a file called appByteArray.txt.

Do a quick visual comparison of the two byte arrays. If any of the first few bytes differ, you probably didn't get the arrays from similar enough computers.

Find the indexes of the "random" bytes in the array

Open CompareArrays.ps1 and paste in the two byte arrays so they end up in $array1 and $array2. Run the script and it will print out the index numbers of elements that differ between the two arrays. These are bytes that seem to be unique for every computer (or Windows installation at least), and must be skipped when the UnpinApps.ps1 script is looking for an app.

Add these two pieces of information to the UnpinApps.ps1 script
Open the UnpinApps.ps1 and create a new entry in the $apps array. Just copy one of the existing entries and clear out the values. One of the byte arrays that you got in the first step goes in "appByteArray" and the index numbers you found in the second step go in "bytesThatDontMatter". Save the script and it's ready for use.

Update 2016-08-05
As of Windows 10 version 1607, which was released to the general public a few days ago, Microsoft has added an officially supported method for pinning and unpinning apps (although you can still not unpin apps that users have pinned manually, only those that are pinned by default).

You can read more about this addition here: https://technet.microsoft.com/en-us/itpro/windows/manage/configure-windows-10-taskbar

Thanks to Robert Grohman for pointing this out in the comments!

Now that pinning and unpinning can be done through officially supported means, the scripts in this post are pretty much rendered unnecessary. I guess the exception would be for those who are stuck on older versions of Windows for the foreseeable future, and maybe if you want to force-remove specific user-pinned apps.


The why and how
We recently started rolling out Windows 10 at work, and one of the pain points that we’ve run into is Microsoft Edge. Not that there is anything wrong with Edge, it seems like a pretty alright web browser, but, well, it’s not Internet Explorer and it does not support addons/plugins. It’s also the default browser in a vanilla Windows 10 installation and it’s pinned to the taskbar. To top things off, the Edge icon is very similar to IE’s icon. If you’re reading this post I guess you already know why these things are problematic in an enterprise environment.

So, being the “group policy guy” in the IT department, I set out to find a way to unpin Edge. At this point I’d never had to deal with unpinning modern/universal Windows apps before, but it seemed like a pretty basic task. How hard could it possibly be?

Pretty hard it turned out, at least if you want to avoid messing up user-pinned apps and applications (which we do). Microsoft simply does not offer a straightforward way to unpin a single app with scripts or group policy. In earlier versions of Windows you could apparently get around this by programmatically “right-clicking” the program’s EXE and invoking one of the available actions from the pop-up menu, but as of Windows 10 that functionality has been removed by Microsoft.

Even if that functionality had still been there it wouldn’t have helped us any with the Edge issue, because with modern apps you can’t just right-click their EXE and pin them to the taskbar. There’s some more magic happening under the hood. At least that is the case with Edge.

Anyway, Windows stores the list of pinned apps and desktop applications in the registry key HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Taskband\Favorites. Regular desktop applications additionally have a shortcut added to %AppData%\Microsoft\Internet Explorer\Quick Launch\User Pinned\TaskBar, but since Edge is an app we don’t have to worry about deleting any shortcuts.

The Favorites registry key is of the type REG_BINARY and contains a binary blob representing the list of pinned taskbar items. That makes it a bit tricky to parse. You can view the hex and character representation of the registry key via the Windows Registry Editor, which shows that it contains at least some data that translates into meaningful ASCII characters.

PowerShell’s Get-ItemProperty Cmdlet allows you to dump and manipulate registry keys, by default as an array of bytes. After a bit of experimentation, I found that each pinned app or application adds a specific number of bytes to the taskbar registry key, and that the registry key is terminated by a single all-ones byte, or 255 in decimal.

The position of each app or application’s bytes in the registry key corresponds with the order of the items on the taskbar. Very simplified, you could say that if I have Edge, Firefox and Notepad pinned to the taskbar, in that order, the registry key contains EdgeFirefoxNotepadEND. If I move Firefox all the way to the left, the key changes to FirefoxEdgeNotepadEND, and if I finally unpin edge the key changes to FirefoxNotepadEND. You see where I’m going with this?
I wrote two basic PowerShell scripts. The first is used to find the byte array for a given app, and the second takes one or more of these byte arrays and removes them from the current taskbar registry key. Finally, it restarts explorer.exe to refresh the taskbar.

At this point I was all smiles, until I went to test the script on a different computer, running the exact same version of Windows with same updates installed. The script didn’t work. Much dismayed, I checked the length of the Edge byte arrays from both computers (they matched), and then converted them to their character representations. To the naked eye they looked the same, but there had to be some bytes in there that differed. I used PowerShell to step through the arrays and compare every single byte, writing out the index numbers if two bytes didn’t match.

The bytes at index 1227 through 1232 differed. I had a look at them in character representation, but they were either garbage or blanks. Testing on a third computer turned up the same results, bytes 1227 through 1232 were different from the two other computers. All right then, I guess we should just ignore those bytes when doing the array comparison.

Since we’re using the Current Branch for Business of Windows 10 at my workplace, we were still on build 10.0.10240-something-something at the time I made this script. This was reflected by Edge’s version number, and thus the byte array that needed to be removed. In an attempt to futureproof the script a little I also added two flavors of the Edge byte array from 10.0.10586 builds of Windows. In the end the script successfully removed Edge from all the computers I tested it on at work, as well as the few couple of 10.0.10586 computers that I have at home.

All right, we’re rid of Edge! That wasn’t so bad, was it? Now we just need to pin Internet Explorer. Surely this has to be officially supported by Microsoft? Nope. And as mentioned, the old “right-click”-workaround has been removed by Microsoft. Good grief, it’s like they hold a personal grudge against me.

So back to the drawing board. What I ended up doing was taking my existing PowerShell script to unpin Edge and modifying it a bit to check if IE was pinned, and if not, pin it. The script first checks if IE is pinned by looking for a small subset of the full IE byte array, a subset that I’m pretty sure will stay the same no matter what version of Windows you’re using. If it doesn’t locate that byte array subset, it creates a shortcut in the aforementioned taskbar shortcut directory and adds a known-to-work IE byte array to the taskbar registry key.

We’re currently testing these two scripts at work and everything looks promising so far. The scripts consistently unpin Edge and pin IE, and no one has had their taskbar broken yet. That doesn’t necessarily mean it’s safe for anyone to put these scripts into production, and it could also be that the byte array for Edge in your particular flavor of Windows differs from the ones I have included with the Edge script. In that case you’ll have to dump the Edge array yourself, figure out which bytes always stay the same and update the script accordingly. Don't blame me if the scripts set your dog on fire, though. You've been warned.

There are several different approaches you could take to getting Edge off the taskbar and replacing it with IE, but this was really the only solution I could find that did not involve any third-party software, could be easily deployed through group policy and would preserve any user-pinned apps. Suggestions for improvements and refinements of the scripts are very welcome, please leave a comment below.

søndag 13. mars 2016

Using someonewhocares.org/hosts to create a BIND domain blacklist

2016-06-17 update:
Switched to using uniq for duplicate removal. Uniq can do case-insensitive matching, sort can't.

Original post:
Some awesome guy named Dan Pollock (pollock@theorem.ca) maintains a list of advertising, malware, shock and otherwise potentially unwanted domains at someonewhocares.org/hosts. I've been using this list for years on most of my private computers, but one day I got tired of manually copying the list into my hosts files and decided to set up a more centralized solution in the form of a Linux box running BIND.

Now I'm not going to do a write-up on how to install BIND since it's readily available in the repositories of most Linux distros and there are already plenty of good tutorials on installing and configuring it. Instead, I will supply the Bash script that I use to convert the hosts file from someonewhocares.org into .zones and .db files for BIND to load.

The end result after you've run this script is that the BIND server will return 127.0.0.1 on lookups for any of the blacklisted domains. It's probably worth pointing out that I'm not mainly a Linux guy and that my Bash skills are not the greatest, so the script probably has a lot of room for improvement and doing things more elegantly. But hey, at least it works. Feel free to leave a comment if you have suggestions for improvements.

Oh, and please note that this script was written on a Raspberry Pi running Raspbian. If you're on a different flavor of Linux I guess it's possible that BIND's config files are in different locations or something else like the service name is different. Adjust the script accordingly.

Make sure that the user you're running the script as has sufficient rights to write to BIND's config directories and restart the BIND service.

If Blogger is messing up the formatting for you, try downloading a plaintext version from here: holmvikit.no/Personal/update-dns-blacklist.sh

##############################################################################
#!/bin/bash

# THE DOMAIN BLACKLIST USED BY THIS SCRIPT IS
# CREATED AND MAINTAINED BY DAN POLLOCK
# (pollock@theorem.ca), NOT BY THE CREATOR OF
# THIS SCRIPT
#
# For more details on Dan Pollock, please see
# http://someonewhocares.org/. For full credits
# on the domain blacklist, please go to
# http://someonewhocares.org/hosts/ and scroll
# down to the bottom of the page.

# For BIND to actually load the blacklisted domains
# you need to add include "/etc/bind/blacklisted.zones";
# to /etc/bind/named.conf.local

# What this script basically does is this:
# 1: Grab a fresh list of nasty domains from
#    someonewhocares.org (the 0.0.0.0 version)
# 2: (Optional) Skip the Windows10 section of
#     the list since it causes problems with
#     Windows Update
# 3: Pull the domain names only from the list
#    of unwanted domains, sort them and remove
#    duplicates
# 4: Create the necessary .zones and .db files
#    for BIND to load the blacklisted domains
#
# Once this is done, BIND will return 127.0.0.1 as
# the IP address for any blacklisted domain.

# Clean up old hosts file and BIND blacklist files
rm hosts
rm /etc/bind/blacklisted.zones
rm /etc/bind/blacklist/*

# Get the list of unwanted domains from someonewhocares.org
wget http://someonewhocares.org/hosts/zero/hosts

# Skip the Windows10 section, since as of 2016-03-13 it
# causes problems with Windows Update
#
# If you want to skip more than one section you should
# probably expand this part of the script so that it
# can take array of unwanted sections or load them from
# a file or something.
mapfile -t domains < hosts

cleanedDomains=""

for i in "${domains[@]}"
do
  if [ "$i" == "#<Windows10>" ]
  then
    skipUntil="#</Windows10>"
    echo "# Skipping section $i"
  fi
  if [ "$skipUntil" != "" ]
  then
    if [ "$i" == "$skipUntil" ]
    then
      skipUntil=""
      echo "# Done skipping section $i"
    fi
  else
    cleanedDomains+="$i\n"
  fi
done

echo -e $cleanedDomains > hosts
# End skip Windows10 section

# Grab the domain names only from lines starting with 0.0.0.0,
# sort them and remove duplicates.
domains=`grep -Po '(?<=0\.0\.0\.0 )(?:[-A-Za-z0-9]+\.)+[A-Za-z]{2,6}' hosts | sort | uniq -iu`

# Create an entry in blacklisted.zones and a db-file for each blacklisted domain
for domain in `echo $domains`;
do
  echo "zone \"$domain\" {type master; file \"/etc/bind/blacklist/$domain.db\";};" >> /etc/bind/blacklisted.zones
  dbline=""
  dbline+="\$TTL 3600\n"
  dbline+="@ IN SOA $domain. info.$domain. (2014052101 7200 120 2419200 3600)\n"
  dbline+="   NS      ns.$domain.\n"
  dbline+="   A       127.0.0.1\n"
  dbline+="*  A       127.0.0.1\n"
  echo -e $dbline >> /etc/bind/blacklist/$domain.db
done

# Remove the hosts file that we used to temporarily
# store blacklisted domains
rm hosts

# Restart the BIND service
service bind9 restart

##############################################################################