Wednesday, May 13, 2009

Playing With Fire

That's usually the first thing anyone says to someone when they're answering the question: "What is the 'registry'?"  They say "it's an evil box of destruction that will destroy your computer and all of mankind.  You're playing with fire!"  Ok, I think I actually heard someone say that once.  It was probably me.

Today's walk down destruction lane will focus on the registry.  And because I try to make everyone happy (and end up making nobody happy as a result) will pain the picture with not one, not two, not even three, but FOUR languages!  Amazing!  How the *$(^^*#$ does he do that?!  Pssstt.  Come closer... I have to whisper this.... it's called... Google.

I've been writing scripts for almost 20 years in a variety of languages, but I still look for the easy way out.  That's what scripting is all about: saving time and saving work.  Programmers look to make something awe-inspiring.  Scripters look to make things that free them up to drink more beer and make jokes about programmers.  Scripters also get to the bar earlier and therefore finish off the snacks before the worn-out programmers show up.  You know: while you're compiling, I'm doing something more fun.

Enough of that gibberish.  It'll melt your mind.  Here are four ways to query a registry key and pull all the sub-keys and values beneath it.  They aren't all purely recursive.  Two of them are, two of them aren't.  The two that are can be considered completely cheating, but who cares.  The VBScript and KiXtart examples are one-deep, not exhaustively recursive.  The CMD and PowerShell examples are.  Let's start with the ugly first, but please read the comments at the end as well?


Option Explicit

' Adapted from ActiveXperts registry script library...
' Click Here

Const strComputer = "."

Const HKEY_LOCAL_MACHINE = &H80000002
Const HKEY_CURRENT_USER = &H80000001
Const HKEY_USERS = &H80000003
Const REG_SZ = 1
Const REG_BINARY = 3
Const REG_DWORD = 4
Const REG_MULTI_SZ = 7

Dim StdOut, oReg
Set StdOut = WScript.StdOut

Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & _
strComputer & "\root\default:StdRegProv")

EnumSubkeys HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Services"

Sub EnumSubkeys(hive, parentkey)
Dim subkey, arrSubKeys
Wscript.Echo "PARENTKEY: " & parentkey
oReg.EnumKey hive, parentkey, arrSubKeys
For Each subkey In arrSubKeys
EnumValueTypes hive, parentkey & "\" & subkey
End Sub

Sub EnumValueTypes(hive, strKeyPath)
Dim i, x, ValueName, arrValueNames, arrValueTypes, strValue, arrValues
Wscript.Echo "SUBKEY: " & strKeyPath
oReg.EnumValues hive, strKeyPath, arrValueNames, arrValueTypes
If VarType(arrValueNames) > 8192 Then
For i=0 To UBound(arrValueNames)
ValueName = arrValueNames(i)
Wscript.Echo vbTab & "Value Name.: " & ValueName
Select Case arrValueTypes(i)
Wscript.Echo vbTab & "Data Type..: REG_SZ"
oReg.GetStringValue hive, strKeyPath, ValueName, strValue
Wscript.Echo vbTab & "Value......: " & strValue
Wscript.Echo vbTab & "Data Type..: REG_EXPAND_SZ"
oReg.GetExpandedStringValue hive, strKeyPath, ValueName, strValue
Wscript.Echo vbTab & "Value......: " & strValue
Wscript.Echo vbTab & "Data Type..: REG_BINARY"
oReg.GetBinaryValue hive, strKeyPath, ValueName, strValue
If VarType(strValue) > 8192 Then
For x = 0 to UBound(strValue)
Wscript.Echo vbTab & "Value......: " & strValue(x)
End If
Wscript.Echo vbTab & "Data Type..: REG_DWORD"
oReg.GetDWordValue hive, strKeyPath, ValueName, strValue
Wscript.Echo vbTab & "Value......: " & strValue
Wscript.Echo vbTab & "Data Type..: REG_MULTI_SZ"
oReg.GetMultiStringValue hive, strKeyPath, ValueName, arrValues
For each strValue in arrValues
Wscript.Echo vbTab & "Value......: " & strValue
End Select
Wscript.Echo vbTab & "Data Type..: " & VarType(arrValueNames)
End If
End Sub


; Adapted from fEnumKey() by Howard A. Bullock
; Click Here

Break ON

$regkey = "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services"

Function EnumSubkeys($parentkey)
Dim $index, $error, $x
$index = 0
Dim $keyname[$index]

If KeyExist($parentkey)
$x = EnumKey($parentkey, $index)
$error = @error
If Not $error And $index > Ubound($keyname)
ReDim Preserve $keyname[$index]
If Not $error
$keyname[$index] = $x
$index = $index + 1
Until $error
$keyname[0] = ""
Exit 2

Function EnumKeyValues($subkey)
Dim $i, $valuename, $v, $vname, $rval, $rtyp
$i = 0
Dim $vname[$i]
? "SUBKEY: $subkey"
$v = EnumValue($subkey, $i)
$err = @error
If Not $err And $i > Ubound($vname)
ReDim Preserve $vname[$i]
If Not $err
$vname[$i] = $v
$rval = ReadValue($subkey, $v)
$rtyp = ReadType($subkey, $v)
$i = $i + 1
? Chr(9)+"ValueName..: $v"
? Chr(9)+"Value......: $rval"
? Chr(9)+"Data Type..: $rtyp"
Until $err


dir hklm:system\currentcontrolset\services -recurse -ea silentlycontinue

CMD Shell:

reg query HKLM\SYSTEM\CurrentControlSet\Services /s 


You might have noticed that the last two examples are just a teensy-weensy bit shorter than the first two.  Just a little.  That's not really a fair comparison.  Comparing the brevity of languages is a slippery slope.  Because they can be described as smoke-and-mirrors in some respects.  Each "language" (or "engine" if you will) comes with a particular (singular or community based) mindset regarding what belongs in the "core" versus what should be left to YOU to add-on.

PowerShell derives much of its potential from cmdlets, which are modular functions which collectively fill-out the PowerShell capability set.  You could do very similar things with KiXtart UDF's (User Defined Functions), which are identical in many respects.  The difference beneath that layer is that PowerShell derives from the .NET services layer, while KiXtart derives from an aggregate of COM and WMI interfaces.  The results almost identical in most respects, but there is only major difference overall: KiXtart is procedural, while PowerShell is object-oriented.  Does that mean that KiXtart is *always* procedural and PowerShell is *always* object oriented?  No.  Cars aren't always cars either.

So, basically, essentially, in other words, and so on, you could produce an almost identical functional expression with KiXtart that you see with PowerShell by simply building (or downloading) a UDF and it would (or could) be as brief and simple.  The REG.EXE command (CMD Shell example) is a compiled, single-purpose executable, rather than a scripting engine or environment.  I included it simply to show that PowerShell is but one way to screw up your registry with the fewest keystrokes.  (A dangerous combination if you drink beer AND a Red Bull in the same hour as you start playing with that.)

As I mentioned in an earlier post, there are trade-offs to each and every "tool" you use.  Some will be faster and easier at some things, but harder to lug around.  A good example of this is CMD shell, which is already pervasive, versus PowerShell, which is still in its infancy of pervasiveness.  Then there's KiXtart which is like a suitcase with a nuclear bomb in it.  Powerful, yet completely portable and fairly lightweight.  PowerShell on the other hand is not lightweight, and requires not only .NET Framework, but it's own installation, in order to crank it up and start using it.  But once it's roots are planted, it rocks.

Well, the goal of this article was to show some simple comparisons and discuss some of the differences between them.  I hope this helps!  Cheers!

Post a Comment