Use PowerShell to Find the History of USB Flash Drive Usage

Use PowerShell to Find the History of USB Flash Drive Usage

  • Comments 6
  • Likes

Summary: Microsoft premier field engineer, Jason Walker, shows how to use Windows PowerShell to get a history of USB drive usage.

Microsoft Scripting Guy, Ed Wilson, is here. I was talking to Jason Walker at the Charlotte Windows PowerShell User Group the other day. I asked him what cool things he was doing with Windows PowerShell, and he discussed a script he had recently written. I encouraged him to write a guest blog about the script. Today’s blog is a result of that conversation.

Photo of Jason Walker

Jason Walker is a premier field engineer (PFE) at Microsoft, and he supports customers in the public arena. His primary job is supporting Exchange Server, but he jumps at the opportunity to flex his Windows PowerShell muscles to resolve any issue that may come up. It does not matter if it is related to Exchange Server. Jason also actively participates in the Charlotte PowerShell Users Group.

Twitter: AutomationJason

USB ports are an awesome resource to any computer. They allow you quickly connect and use accessories such as mice, keyboards, and storage devices, just to name a few. However, USB storage devices are a popular vector bad guys use to get nefarious code onto a machine. With my customers being in the public sector, security is a top priority, and USB storage devices are not allowed. We could disable USB ports all together, but that would eliminate the ability to use other USB devices. Therefore, users are told to simply not use USB storage devices. The need came up to see if the users are playing by the rules and Windows PowerShell was the answer to that need.

“How do we find out if a USB storage device has been connect to a computer?” you ask. We look in the registry, of course. When a USB storage device is inserted into a machine, the USBSTOR key is created in the registry, and everything the operating system needs to know about that storage device is contained in that key. This is the complete path:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\USBSTOR

Image of menu

When we expand the USBSTOR key, we see all the USB storage devices that have been used on the computer.

Image of menu

We see here that 15 different USB storage devices have been used on this machine. I know what you are thinking: “Whoever owns this machine is not very security conscience!”

By looking at the subkey names, we can get an idea about what kind of storage device was used, but the data isn’t easily readable. When we dig deeper, we find a FriendlyName property that is easily readable.

Image of menu

Now we can see that a “SanDisk U3 Cruzer Micro USB Device” was used on this machine.

To get this information, all we need Windows PowerShell to do is start from the USBSTOR key, recurse down two subkeys, and grab the FriendlyName property. There are a couple ways we can get this data. We could do it in one line like this:

Image of command output

But if we want to get this data from a remote machine, this will not work unless Windows PowerShell remoting is enabled—and it most cases it is not. I believe the method that will work in the most number of scenarios is the Microsoft.Win32.RegistryKey class.

Note   If you have Windows PowerShell remoting enabled, see last week’s Hey, Scripting Guy! blogs for a different approach to working with the registry. Saturday’s blog, Use PowerShell to Easily Modify Registry Property Values, contains links to the entire series.

I will explain the code that does all the heavy lifting.

First, starting at USBSTOR, we get all the subkeys:

$Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive,$Computer)

$USBSTORKey = $Reg.OpenSubKey($Key)

$USBSTORSubKeys1 = $USBSTORKey.GetSubKeyNames()

Then we go through each subkey ($USBSTORSubkeys1) and collect the child subkeys. We store them in $SubKeys2 as shown here:

ForEach($SubKey1 in $USBSTORSubKeys1)

{          

  $Key2 = "SYSTEM\CurrentControlSet\Enum\USBSTOR\$SubKey1"

  $RegSubKey2 = $Reg.OpenSubKey($Key2)

  $SubkeyName2 = $RegSubKey2.GetSubKeyNames()      

  $Subkeys2  += "$Key2\$SubKeyName2"

  $RegSubKey2.Close()               

  }#end foreach SubKey1

Now we go through each Key in $Subkey2 and grab the FriendlyName property of the key. The value of the property and the name of the computer are stored in a custom object so we can easily send our data to Export-CSV or filter with Where-Object.

ForEach($Subkey2 in $Subkeys2)

 {         

  $USBKey     = $Reg.OpenSubKey($Subkey2)

  $USBDevice  = $USBKey.GetValue('FriendlyName')         

  $USBDevices += New-Object –TypeName PSObject -Property @{

            USBDevice = $USBDevice

            Computer = $Computer

             }

         $USBKey.Close()                                                                                       

  }#end foreach SubKey2

A few things to note are that the Remote Registry service must be running on the remote machine. I added a Write-Progress cmdlet because when I first wrote this script it ran against hundreds of machines. The Test-Connection cmdlet is not needed because the connection to the remote machine is inside the Try\Catch. However, during testing, it took 30+ seconds for the script to move to the next computer when trying to connect to a machine that was not online, so I added that as an option.

The script is written as a function, and it has comment-based Help with examples. Because it is written as a function, it will need to be dot-sourced. This is done by placing a dot and a space in front of the path to the script. When this is done, you can use the function just as you would a native Windows PowerShell cmdlet. Here is an example of how this is done:

Image of command output

Thanks, and I hope you can find this script useful when it’s time for you to flex your Windows PowerShell muscles.

~Jason

 

The complete script can be downloaded from the Script Repository. Thank you for a great guest blog. I have often seen this registry key, but never thought about using it in the way you have here. Awesome job.

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
  • <p>Jason, Awesome article, I love seeing all the ways PowerShell is being used. &nbsp;I really like the fact that you explained the function principle at the end, with screen-shots. &nbsp;I feel sometimes folks don&#39;t completely understand how that works and I believe you cleared it up wonderfully.</p>

  • <p>Very nice article!</p>

  • <p>Hi Jason,</p> <p>thank you for a very good example, showing how to exploit PowerShell ( and .Net ) capabilities to retrieve registry data! I didn&#39;t even know, that there is an USB history available!!!</p> <p>There is one thing, I would probably change in your script:</p> <p>I would make more use of the pipeline inside the process block.</p> <p>( This helps to get rid of the arays where the intermediate results are stored. )</p> <p>My alternative &quot;Try-Block&quot; looks like this:</p> <p>Try {</p> <p> &nbsp; $Reg = [Microsoft.Win32.RegistryKey]::OpenRemoteBaseKey($Hive,$Computer)</p> <p> &nbsp; $USBSTORKey = $Reg.OpenSubKey($Key)</p> <p> &nbsp; $USBSTORKey.GetSubKeyNames() | </p> <p> &nbsp; ForEach-Object {</p> <p> &nbsp; &nbsp; &nbsp; $subKey1 &nbsp; &nbsp;= &quot;$Key\$_&quot;</p> <p> &nbsp; &nbsp; &nbsp; $RegSubKey1 = $Reg.OpenSubKey($subKey1)</p> <p> &nbsp; &nbsp; &nbsp; $RegSubKey1.GetSubKeyNames() | </p> <p> &nbsp; &nbsp; &nbsp; ForEach-Object {</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $subKey2 &nbsp; = &quot;$subKey1\$_&quot;</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $USBKey &nbsp; &nbsp;= $Reg.OpenSubKey($subKey2)</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $USBDevice = $USBKey.GetValue(&#39;FriendlyName&#39;)</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; If($USBDevice) { &nbsp; </p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; New-Object -TypeName PSObject -Property @{</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; USBDevice = $USBDevice</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Computer &nbsp;= $Computer</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</p> <p> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } $USBKey.Close() &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</p> <p> &nbsp; &nbsp; &nbsp; } $RegSubKey1.Close() &nbsp; &nbsp; </p> <p> &nbsp; } $USBSTORKey.Close()</p> <p>} #end try</p> <p>It&#39;s just another way to the same results :-)</p> <p>Klaus.</p>

  • <p>Great script, although I get an error message about the registry service even though it still works and gets the data.</p> <p>Too bad Windows doesn&#39;t put a timestamp in the registry for when it was last used.</p>

  • <p>@Danny Maas thanks for the feedback. &nbsp;I updated the script. &nbsp;What was happening is there are multiple GUIDs under the Key that has the &quot;Disk&amp;Ven&quot; in the key name. &nbsp;Try the updated script and see if that works for you.</p> <p>Thanks again.</p>

  • <p>Can you tel me how to delete these entries.</p>