Leverage Registry Key Time Stamps via PowerShell

Leverage Registry Key Time Stamps via PowerShell

  • Comments 4
  • Likes

Summary: Guest blogger, Rohn Edwards, talks about leveraging registry key time stamps via Windows PowerShell.

Microsoft Scripting Guy, Ed Wilson, is here. Today we wrap-up a four-part series from Rohn Edwards, co-founder of the Mississippi PowerShell User Group. If you have not already done so, you may want to read the previous posts in the series:

Note  To follow today’s examples, you’ll need the Add-RegKeyMember and Get-ChildItem functions from the previous two posts.

If you’ve been following along, you now have the ability to see the last time that a registry key was modified from within Windows PowerShell. Today I want to explore a few things that you can do with that information.

First, let’s go over some of the limitations of looking at the last modified property of a registry key. Like the name suggests, the last modified time is the last time that something about the key was changed. It could be that a value was changed (or multiple values were changed), that a subkey key was created (or deleted), or that the security descriptor was changed. You can’t use any of the registry key’s properties to tell what was changed—you only know that there was some sort of a change (you can audit changes to the registry by using object access auditing, but that’s not today’s topic). The registry doesn’t keep track of when a key was created either.

If you understand the limitations, you can still gather some pretty useful information. Today I’m going to cover a few simple examples of using the last modified property for troubleshooting, auditing, and forensics.

Troubleshooting

I’ve often found myself troubleshooting an issue with an application that has unexpectedly stopped working. Knowing the last time it worked, I’ll sometimes look for any files inside the program’s installation and application data folders that have been modified since that time. It’s usually a long shot, but I occasionally get lucky and find a specific configuration file that I could further research, and ultimately fix the issue. That same scenario can play out with registry keys. Again, you can’t pinpoint values that were changed, so searching the registry can be even more of a long shot than the file system, but it can still be useful.

Let’s say, for example, that something weird started going on with Office a few hours ago. You’ve narrowed it down to something under HKCU:\SOFTWARE\Microsoft\Office. You could run a command like this:

dir HKCU:\SOFTWARE\Microsoft\Office -Recurse |

Where-Object LastWriteTime -gt (Get-Date).AddDays(-1) |

Select-Object Name, LastWriteTime |

Sort LastWriteTime

And, you would get output that looks like this:

Image of command output

Note  You have to use the Get-ChildItem proxy function that was discussed yesterday to use the LastWriteTime property. If you don’t want to use the proxy, you need to add a call to the Add-RegKeyMember function (from Reusing PowerShell Registry Time Stamp Code) in the pipeline.

Auditing

The registry obviously contains a ton of information that is useful for auditing. Take a list of installed software, for example. Knowing the name and version of an application, and also the last time the installation was modified could be very helpful. It just so happens that you can get a pretty accurate list of installed software by querying the child keys under:

HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall

Some of these keys contain an InstallDate value, but not all of them. Looking at the last modified time of each key gives you a pretty reasonable guess as to the last time the application’s installation was modified. You can’t tell if it’s when the application was installed or patched, or if the key was manually modified, but knowing the time stamp can still be useful.

Knowing that it’s not perfect, here’s an example of how getting all of that information:

"", "\Wow6432Node" |

    ForEach-Object { Get-ItemProperty "HKLM:\SOFTWARE$_\Microsoft\Windows\CurrentVersion\Uninstall\*" -ErrorAction SilentlyContinue } |

    Where-Object DisplayName | # Some keys don't have display names

    Select-Object DisplayName, Publisher, DisplayVersion, InstallDate, @{Name="LastModified"; Expression={ (Add-RegKeyMember $_.PsPath).LastWriteTime }} |

    Sort-Object DisplayName

And here’s what a sample output would look like:

Image of command output

Notice that I’m using Select-Object to create a calculated property. That calculated property is calling the Add-RegKeyMember function to get the last modified time of the key.

Forensics

First, let me mention that I wouldn’t suggest using this for real forensics work when you’re trying to piece together valuable information. Obviously, that wouldn’t be done on a live system. (Maybe one day I’ll do a blog post about working with offline registry files by using nothing but Windows PowerShell.) You can still do a little mock investigating for fun, though.

Today, I’ll cover an example of listing the removable drives that are used on a system, and the last time each one was plugged in.

Warning  I’ve got to start with the same warning I’ve already mentioned: You really can’t depend on this information as being completely accurate. If you run the following examples, plug in a removable drive and rerun the commands. You should see that the output is properly updated. If you’re looking at older drive information, though, you can’t be sure that the keys weren’t modified by something or someone else. Also, you can’t be sure that some of the assumptions that I’ve made here (I also got some help from the Internet) are correct for all versions of Windows (or ANY versions of Windows, for that matter).

Hardware information for the computer is stored under the following key: HKLM:\SYSTEM\CurrentControlSet\Enumy. Any USB device that you plug-in to your computer should get its own set of keys under the USB subkey. To get a list of all of the USB hard drives that have been used on your computer, you could run this command (you can remove the where statement if you want more than hard drives listed):

Get-ItemProperty HKLM:\SYSTEM\CurrentControlSet\Enum\USB\*\* |

    Where-Object Service -eq USBSTOR |

    Select-Object @{Name="DeviceDesc"; Expression={ $_.DeviceDesc -split ";" | select -last 1 }},

        @{Name="SerialNumber"; Expression={ $_.PsChildName }},

        @{Name="LastModified"; Expression={ (Add-RegKeyMember $_.PsPath).LastWriteTime }}

While testing different USB devices, it seems that hard drive keys get updated each time they’re plugged in, but non-hard drive devices don’t all seem to have their keys updated. Here’s what the command looks like on my computer (I removed the serial numbers):

Image of command output

You could actually take the time to look up or work out how several of the different HKLM:\SYSTEM keys are related, and figure out a lot more information about each of the removable drives (or any piece of hardware, for that matter)—for example, the first time it was connected, or a better name or description than the previous simple command shows.

Thanks for taking the time to read my series of posts on this topic! Hopefully you’ll find a use for being able to see the last-modified time of a registry key. If not, maybe the process of developing the tools to do this was useful to you.

~Rohn

Thank you, Rohn, for four great blog posts about working with registry key time stamps.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • The command
    "dir HKCU:\SOFTWARE\Microsoft\Office -Recurse |
    Where-Object LastWriteTime -gt (Get-Date).AddDays(-1) |
    Select-Object Name, LastWriteTime |
    Sort LastWriteTime"
    doesnt work for me.

    Get-ChildItem : A positional parameter cannot be found that accepts argument 'LastWriteTime'.
    At line:1 char:4
    + dir <<<< HKCU:\SOFTWARE\Microsoft\Office -Recurse Where-Object LastWriteTime -gt (Get-Date).AddDays(-1) Select-Object Name, LastWriteTim
    e Sort LastWriteTime
    + CategoryInfo : InvalidArgument: (:) [Get-ChildItem], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand