Entries in Powershell (6)

Thursday
Sep292011

Back to Basics: Brute Force in Powershell

Most of you have heard some variation of this problem:

A farmer has pigs and chickens. When the farmer walks out in the yard he sees 20 heads and 56 legs. How many pigs and chickens does the farmer have?

Based on the information given we can determine a few basic truths:

  1.  20 heads. I think we can assume we have no decapitated pigs or chickens so the number of pigs + number of chickens is equal to 20.
  2. 56 legs. 4*number of pigs + 2*number of chickens is equal to 56.

So how are we going to solve this in a programmatic way? Lets try a brute force algorithm. That's just another way to say we are going to enumerate and check over and over until we hit a solution. Remember this code is for learning and doesn't follow best practices.

function solve {            
 param(            
 [int]$numLegs,            
 [int]$numHeads            
 )            
             
 foreach ($numChicks in (0..$numHeads + 1)) {            
  $numPigs = $numHeads - $numChicks            
  $totLegs = 4*$numPigs + 2*$numChicks            
  if ($totLegs -eq $numLegs) {            
   return $numPigs, $numChicks            
  }            
 }            
 return            
}            
            
function barnYard {            
 $heads = Read-Host "Enter the number of heads"            
 $legs = Read-Host "Enter the number of legs"            
 $pigs, $chickens = solve -numLegs $legs -numHeads $heads            
 if ($pigs -eq $null) {            
  Write-Host "No pigs"            
 } else {            
  Write-Host "Number of pigs", $pigs            
  Write-Host "Number of chickens", $chickens            
 }            
}

Above is two functions, solve and barnYard. In programming this is called "decomposition". Basically we want to isolate components and break up the code so we can have separation and reuse. Functions also allow us to capture common patterns of computation and refer to it by name. But you already knew that. So lets throw these two functions in a file called farmyard.ps1 and source it.

PS H:\Development\Powershell> . .\farmyard.ps1

Now we can call the functions within the file and solve our farmyard mystery:

PS H:\Development\Powershell> barnYard            
Enter the number of heads: 20            
Enter the number of legs: 56            
Number of pigs 8            
Number of chickens 12

I'll leave you with a few words from Wikipedia on the subject:

Brute-force search is typically used when the problem size is limited, or when there are problem-specific heuristics that can be used to reduce the set of candidate solutions to a manageable size. The method is also used when the simplicity of implementation is more important than speed. This is the case, for example, in critical applications where any errors in the algorithm would have very serious consequences; or when using a computer to prove a mathematical theorem. Brute-force search is also useful as "baseline" method when benchmarking other algorithms or metaheuristics. Indeed, brute-force search can be viewed as the simplest metaheuristic.

Tuesday
Sep272011

Back to Basics: Exhaustive Enumeration in Powershell

I'm often overheard at work and customer engagements spreading the gospel about Powershell, encouraging everyone from the Junior SA to the most senior "engineer" to take advantage of this powerful language. Anyone can pick Powershell up and become quite productive in no time. One of the things I noticed however was a lack of fundamentals in the newly initiated. So I figured why not do my part and provide a few posts I'm calling "Back To Basics". These are foundational to any langauge, not just Powershell, so pick your poison. Feel free to post your samples in the comments.

Now lets get started with our first lesson, Exhaustive Enumeration.

So what is it? Put simply, exhaustive enumeration is trying all "possible" values until you find the solution.

I always find a problem useful for learning so lets use something simple we can all understand. Finding the square root of a perfect square.

So whats a square and a square root?

A square is a number multiplied by itself. So 3 x 3 = 9

Ok enough of that, how about some code?

PS C:\> [math]::sqrt(9)            
3

No, no, no. Thats not it. The point of this code is to learn. We all know there are better ways to do things, but remember we are forcing a point here, so the code is representing that. Alright back to the matter at hand:

# Find the square root of a perfect square            
$x = Read-Host "Please enter a positive integer"            
$ans = 0            
if ($x -as [int] -and $x -ge 0) {            
 while ($ans * $ans -lt $x) {            
  $ans = $ans + 1            
  Write-Verbose "Trying [$ans]"            
 }            
 if ($ans*$ans -ne $x) {            
  Write-Host "$x is not a perfect square!"            
 } else {            
  Write-Host $ans            
 }            
} else {            
 Write-Host "$x is a negative number or a non-integer."            
}

Now lets see it in action:

PS H:\Development\Powershell> $VerbosePreference = "Continue"            
PS H:\Development\Powershell> .\ExhaustiveEnumeration.ps1            
Please enter a positive integer: 9            
VERBOSE: Trying [1]            
VERBOSE: Trying [2]            
VERBOSE: Trying [3]            
3

So most of you are thinking man that must be slow! So lets take a larger number, say 1515361 and see how slow it is:

PS H:\Development\Powershell> Measure-Command {.\ExhaustiveEnumeration.ps1}            
1231            

Days              : 0            
Hours             : 0            
Minutes           : 0            
Seconds           : 0            
Milliseconds      : 131            
Ticks             : 1310124            
TotalDays         : 1.51634722222222E-06            
TotalHours        : 3.63923333333333E-05            
TotalMinutes      : 0.00218354            
TotalSeconds      : 0.1310124            
TotalMilliseconds : 131.0124

131 milliseconds, not bad. See computers are pretty good at this type of thing! Yes there are much more efficient ways to solve this, but you'll have to wait for a future post.

Monday
Jul042011

Reading the LastWriteTime of a registry key using Powershell

At work recently I needed to pull together some information from the registry of a few thousand machines and include the last time the key had been updated. Lately I've been turning to Powershell more and more for my day to day tasks and this time was no different. However this simple task turned out to not be so easy, and it all revolved around acquiring the LastWriteTime of the registry keys.

Digging through WMI and .NET proved less fruitful than I had hoped, so off to Google I went. It seemed everyone had the solution if you wanted to query the machine locally, but with thousands of hosts in my Enterprise that wasn't going to work. Plus, who doesn't enjoy a good challenge. One particular script was very useful in pointing my team in the right direction, posted by Tim Medin over at blog.securitywhole.com. So we decided to adapt and modify his script to work with remote hosts.

I've only tested against a few machines at home, but I wanted to share it while I had time. I will update the post if I find issues in the production environment. And as always please feel free to weigh in and provide comments!


Download Script

function Get-RegKeyLastWriteTime {            
 <#
	.SYNOPSIS
	Retrieves the last write time of the supplied registry key

	.DESCRIPTION
	The Registry data that a hive stores in containers are called cells. A cell 
	can hold a key, a value, a security descriptor, a list of subkeys, or a 
	list of key values.

	Get-RegKeyLastWriteTime retrieves the LastWriteTime through a pointer to the
	FILETIME structure that receives the time at which the enumerated subkey was
	last written. Values do not contain a LastWriteTime property, but changes to
	child values update the parent keys lpftLastWriteTime.
	
	The LastWriteTime is updated when a key is created, modified, accessed, or
	deleted.

	.PARAMETER ComputerName
	Computer name to query

	.PARAMETER Key
	Root Key to query

	HKCR - Symbolic link to HKEY_LOCAL_MACHINE \SOFTWARE \Classes.
	HKCU - Symbolic link to a key under HKEY_USERS representing a user's profile
	hive.
	HKLM - Placeholder with no corresponding physical hive. This key contains
	other keys that are hives.
	HKU  - Placeholder that contains the user-profile hives of logged-on
	accounts.
	HKCC - Symbolic link to the key of the current hardware profile

	.PARAMETER SubKey
	Registry Key to query

	.EXAMPLE
	Get-RegKeyLastWriteTime -ComputerName testwks -Key HKLM -SubKey Software

        .EXAMPLE
	Get-RegKeyLastWriteTime -ComputerName testwks1,testwks2 -SubKey Software

	.EXAMPLE
	Get-RegKeyLastWriteTime -SubKey Software\Microsoft

	.EXAMPLE
	"testwks1","testwks2" | Get-RegKeyLastWriteTime -SubKey Software\Microsoft `
	\Windows\CurrentVersion

	.NOTES
	NAME: Get-RegKeyLastWriteTime
	AUTHOR: Shaun Hess
	VERSION: 1.0
	LASTEDIT: 01JUL2011
	LICENSE: Creative Commons Attribution 3.0 Unported License
	(http://creativecommons.org/licenses/by/3.0/)

	.LINK
	http://www.shaunhess.com
	#>            
            
 [CmdletBinding()]            
            
 param(            
 [parameter(            
 ValueFromPipeline=$true,            
 ValueFromPipelineByPropertyName=$true)]            
 [Alias("CN","__SERVER","Computer","CNAME")]            
 [string[]]$ComputerName=$env:ComputerName,            
 [string]$Key = "HKLM",            
 [string]$SubKey            
 )            
            
 BEGIN {            
  switch ($Key) {            
   "HKCR" { $searchKey = 0x80000000} #HK Classes Root            
   "HKCU" { $searchKey = 0x80000001} #HK Current User            
   "HKLM" { $searchKey = 0x80000002} #HK Local Machine            
   "HKU"  { $searchKey = 0x80000003} #HK Users            
   "HKCC" { $searchKey = 0x80000005} #HK Current Config            
   default {            
   "Invalid Key. Use one of the following options:
			HKCR, HKCU, HKLM, HKU, HKCC"}            
  }            
            
  $KEYQUERYVALUE = 0x1            
  $KEYREAD = 0x19            
  $KEYALLACCESS = 0x3F            
 }            
 PROCESS {            
  foreach($computer in $ComputerName) {            
              
$sig0 = @'
[DllImport("advapi32.dll", SetLastError = true)]
  public static extern int RegConnectRegistry(
  	string lpMachineName,
	int hkey,
	ref int phkResult);
'@            
  $type0 = Add-Type -MemberDefinition $sig0 -Name Win32Utils `   -Namespace
RegConnectRegistry -Using System.Text -PassThru $sig1 = @' [DllImport("advapi32.dll", CharSet = CharSet.Auto)] public static extern int RegOpenKeyEx( int hKey, string subKey, int ulOptions, int samDesired, out int hkResult); '@ $type1 = Add-Type -MemberDefinition $sig1 -Name Win32Utils `
-Namespace RegOpenKeyEx -Using System.Text -PassThru $sig2 = @' [DllImport("advapi32.dll", EntryPoint = "RegEnumKeyEx")] extern public static int RegEnumKeyEx( int hkey, int index, StringBuilder lpName, ref int lpcbName, int reserved, int lpClass, int lpcbClass, out long lpftLastWriteTime); '@ $type2 = Add-Type -MemberDefinition $sig2 -Name Win32Utils `
-Namespace RegEnumKeyEx -Using System.Text -PassThru $sig3 = @' [DllImport("advapi32.dll", SetLastError=true)] public static extern int RegCloseKey( int hKey); '@ $type3 = Add-Type -MemberDefinition $sig3 -Name Win32Utils `
-Namespace RegCloseKey -Using System.Text -PassThru $hKey = new-object int $hKeyref = new-object int $searchKeyRemote = $type0::RegConnectRegistry($computer, $searchKey, ` [ref]$hKey) $result = $type1::RegOpenKeyEx($hKey, $SubKey, 0, $KEYREAD, ` [ref]$hKeyref) #initialize variables $builder = New-Object System.Text.StringBuilder 1024 $index = 0 $length = [int] 1024 $time = New-Object Long #234 means more info, 0 means success. Either way, keep reading while ( 0,234 -contains $type2::RegEnumKeyEx($hKeyref, $index++, ` $builder, [ref] $length, $null, $null, $null, [ref] $time) ) { #create output object $o = "" | Select Key, LastWriteTime, ComputerName $o.ComputerName = "$computer" $o.Key = $builder.ToString() # TODO Change to use the time api $o.LastWriteTime = (Get-Date $time).AddYears(1600).AddHours(-4) $o #reinitialize for next time through the loop $length = [int] 1024 $builder = New-Object System.Text.StringBuilder 1024 } $result = $type3::RegCloseKey($hKey); } } } # End Get-RegKeyLastWriteTime function
Page 1 2