23 February 2015

I learned something new last Friday as well as had another idea driven home again. I like it when that happens. It’s odd to learn something yet periodically “realize” it all over again when life has need of that lesson.

I started working again on a scripting promise I made a while ago about sharing the ability to run an encrypted script without having to write the unencrypted contents to disk, thus making the contents of your script more secure. I’ve had the concept in my head and most of the proof it could be done, I just have to work on one very irritating issue with arrays.  The other thing I needed to work on, and solved, was generating a random string.  PowerShell has a native cmdlet to get a random number but not one for a string.  I needed a string, so that I could randomly generate the IV that goes with each script.  It’s generally accepted that knowing the IV doesn’t make it less secure so hard coding it to the resultant script isn’t a bad thing.  However, it IS bad to always use the same IV.  I figured it couldn’t be hard to write a simple function to generate a random string; it really wasn’t.  Initially it was frustrating, though.

I started out with the idea I could use the get-random to randomly generate a 0 or 1 and then loop through that 8 times to generate an 8 bit binary code that I could then convert to a string.  Figuring out how to make that 8 bit binary code into a string took a bit, and required multiple conversions of the data.  My searching proved more fruitful than I intended.  The one thing I learned was all of the different conversion methods available via .NET. Specifically, converting characters from decimal/hex/binary to whichever form is needed. It makes sense that they exist now that I think about it, but at the time I had never had a need for them and didn’t know they existed.  This lead me to my recurring life lesson:  it’s most invaluable being able to think differently.  What could have continued to be difficult and maybe (for all intents and purposes) impossible, became an elegant solution just because I started to look at my situation from a different point of view.

I didn’t need to generate a rand byte of 1’s and 0’s because each ASCII character also has a decimal (and hex) value as well.  So instead of using get-random 8 times to produce 1 ASCII char, I just needed to use it once to generate it’s decimal value.  I didn’t want the non-character values in ASCII so I gave it the range of 33-127.  The get-random does not include the max number (127, which is DEL and I don’t want) but it does include the minimum, 33 (which I do want and is !).  I don’t know why it behaves this way.. it doesn’t make any sense.

The final\ script, below, ended up being only half the lines of the previous, unfinished script.  I hope you find it useful.  To use it, you just call the function and pass it the number of characters you want to use.  ex)  create-randomString 32

Function create-randomString {
    param(
    [Parameter(Mandatory=$true,Position=1)]
    [string]$length

)

    $finalString = @()
    For ($i=1; $i -le $length; $i++) {
        $finalString += [CHAR][BYTE](get-random -max 127 -min 33)
    }

    Write-Host (-join $finalString)

}

8 February 2015

Got tired of the old gimmicky design and created something new and simple. Still needs a few tweaks here and there I think but I’m fairly happy with it.

I’m hoping to get images, instead of just text, for the Most Popular items across the top… but we’ll see if/when I get there 🙂


12 January 2015

Update 5-27-17 — This wont work with Seasons 19 and after as they have removed the download link from each episode page.  Also, Revision3 no longer exists (got folded into Seeker, whatever that is), so who knows how long the older episodes from 1-18 will continue to exist.  The actual download mp4 files were/are hosted on the revision3.com domain.

I’ve never been able to find a REALLY good podcatcher. I use Juice and it works good… as long as it’s always running. I thought I had it set to startup when I login but apparently I hadn’t. I fell off keeping up with Hak5 and then one day I go to start up again and I have no current episodes! It had been months. I left it alone again and there I was yesterday wanting them again. The RSS feed only does the current season so I couldn’t use iTunes or another app to “Download All” as they only saw the current season. I needed to go back two seasons, too. Enter PowerShell.

I figured I could do it. Fuddled with Invoke-WebRequest but it doesn’t work great. The Links property was always missing the link to the HD video file. I ended up having to create a WebClient and download the webpage, then parse it for the HD video file. It didn’t take too long… if you don’t count the typo I made which kept making me download the page for episode 1 of season 1, lol (No HD back then…). I added params and comments and spruced it up a bit for general internet consumption.

###################################################################
##  Description:  Downloads all of the HD videos for the given season of Hak5
##
##  This script is provided as is and may be freely used and distributed so long as proper
##  credit is maintained.
##
##  Written by: thegeek@thecuriousgeek.org
##  Date Modified:  01-12-2015
###################################################################

param(
	[Parameter(Mandatory=$true,Position=1)]
	[string]$season,
	
	[Parameter(Mandatory=$true,Position=2)]
	[int]$first,
	
	[Parameter(Mandatory=$true,Position=3)]
	[int]$last
)

# First I need to check if any of the episode numbers are less than 10
# if so, I need to add a 0 in there to get the correct episode numbers.
If ($first -lt 10) { 
	[int]$first = "$($season)0$first"
}
Else {
	[int]$first = "$($season)$first"
}

If ($last -lt 10) { 
	[int]$last = "$($season)0$last"
}
Else {
	[int]$last = "$($season)$last"
}

# Here I just setup paths for easy use later. Be sure to set Drive to your own drive letter!
$saveDir = "Drive:\Podcasts\Hak5"
$seasonDir = "$saveDir\Season $season"
$url = "http://hak5.org/episodes/hak5-"

# Keeping directory structure.  I test to see if the folder "Season $season"
# exists and if it doesn't it creates it.  The web client will not create it.
If (!(Test-Path $seasonDir)) {
	New-Item $seasonDir -ItemType Directory
}

# Use the first and last episode numbers to go through a loop.  Each one
# is used to create the final URL that will be downloaded for it's source code.
# Thankfully Hak5 is organized and this is easy to do!
For ($i = $first; $i -le $last; $i++) {
	write-host "Working on episode $i"
	$wc = new-object system.net.webclient
	$wc.DownloadFile("$($url)$i","TempLocation")    # I build the URL using the base path I had above and combine it with the episode number from the current iteration of the loop
	$sourceCode = get-content TempLocation    # Be sure to make TempLocation your own path!
	ForEach ($line in $sourceCode) {
		$boolResponse = $line -match "http(.*?)hd720p30\.h264\.mp4"    # This regex expression is saying match a string http (with anything here in the middle) and is followed by hd720p30.h264.mp4
		If ($boolResponse) {
			$fileURL = $matches.0     # the system variable $matches keeps record of all matches you've made.  The query above gives two results, the first is the one I want (complete URL).
			$fileName = $fileURL.Split("/")[$_.Length - 1]     # I need a filename to save as, so I grab the one out of the URL
			$wc.DownloadFile($fileURL,"$seasonDir\$fileName")
		}
	}
}

And then to call it, run the script with parameters such as: DownloadHak5Season.ps1 16 1 26, which tells it to download season 16, episodes 1-26


12 January 2015

I’ve only recently discovered the “multiple assignment” feature in PowerShell via the Windows PowerShell blog. I’m not sure what I was searching for when I stumbled upon it but my first thought when I saw it was “I bet this will make modifying arrays easy!” As some of you may be aware, modifying arrays of object type “string” are a pain in the butt in PowerShell and unless you create the variable as a Collections “ArrayList” object you’re going to be struggling. But, the beforementioned feature makes it so much easier!

For example, I needed to remove 1 line from an INI file on a PC and the potential for doing this to many PC’s prompted me to write a script. I could do the following:

$originalFile = get-content path-to-INI-file

This gets the entire contents of the file. Get-Content is a widely used cmdlet and it stores the content into a string-type array. That’s not such a pain anymore to work with when I want to remove an element from it. First I would search for whatever I want to remove and find it’s index:

$removeIndex = $originalFile.IndexOf($originalFile -match “LogLevel=Finest”)
$newFile = $originalFile[0 .. ($removeIndex – 1)],$originalFile[($removeIndex + 1) .. $originalFile.count]
Set-Content path-to-INI-file $newFile

I could have easily overwritten $originalFile instead of creating $newFile. The gotcha above is that your search term has to be unique. If the match returns multiple results the “indexOf” method will break. You can add the index to the end of the match by adding [] if you know which match it will be ( $orginalFile.IndexOf(($originalFile -match “LogLevel=Finest”)[0]) will return the first match).

This made my life so much easier when I had to write this script to modify multiple configuration files to flip systems to a test mode.


26 October 2014

While planning to develop some scripts that would be meant for others to use I had a need to implement a timeout when asking for input (and to go with a default value).  I didn’t think it had a timeout, but I looked at the documentation on read-host anyway and sure enough, no timeout.  I then took to Google to see if there was anything (I wasn’t feeling in a creative mood and just wanted to do it) that someone else had written to do the job.  I was quite surprised that I couldn’t find anything already written.  I found a few scripts that just waited for a keystroke to be entered before continuing on with the script.  Nothing, though, that could use a promtp like read-host and sit at that prompt until someone started entering something.  So I had to create something myself; it proved surprisingly simple.

I decided that using the detection if a key was available from those other scripts was a great way to detect the moment the end user started typing something.  I knew I’d need a loop, then a break from that loop, and most difficult to capture that first key of input and pass it along to the next section of code, display it for the end user, and also capture the rest of what they typed.  I wanted to do it all seamlessly so it looked exactly like using the built in read-host cmdlet.  I quickly sketched up some psuedo code on my whiteboard and then set out to writing the code.

Like I said, it wasn’t too difficult.  The hardest part was figuring out how to make backspacing appear on the screen as the user typed.  I inadvertently stumbled upon a nice feature when choosing to use a character array instead of creating the string itself on the fly.  Reading each key and adding it to the char array also records the backspaces.  When I use the -join operator to turn the char array into a string, it processes the backspaces and the resultant string does not contain the characters that were backspaced.  Had I been building the string on the fly, I would have had to write extra code to account for that.  I was quite happy when I realized I wouldn’t have to.

The one feature not implemented is the ability to use -assecurestring.  Encryption still makes my brain freeze and I didn’t even try to implement it.  I’m sure I could hack my way through it but I usually prefer to understand something a little better before I implement it.  To contradict myself, though, I did write encryption/decryption scripts for protecting your scripts and even importing the function inside it so it can be used straight away in the shell.  Hopefully I’ll get around to posting those soon.

I think the one thing not to change here is the millisecond sleep when waiting for input.  Unless you go lower… the lower it is the more seamless the typing experience for the end user.  Even at 250 there is a slight delay in writing that first character to the screen.. especially for us fast touch-typists.  It’s pretty seamless though and I’m happy with it.

Anyway, enough gabbing, here’s the code.  It’s fully commented so it should explain everything!

Function read-HostTimeout {
###################################################################
##  Description:  Mimics the built-in "read-host" cmdlet but adds an expiration timer for
##  receiving the input.  Does not support -assecurestring
##
##  This script is provided as is and may be freely used and distributed so long as proper
##  credit is maintained.
##
##  Written by: thegeek@thecuriousgeek.org
##  Date Modified:  10-24-14
###################################################################

# Set parameters.  Keeping the prompt mandatory
# just like the original
param(
	[Parameter(Mandatory=$true,Position=1)]
	[string]$prompt,
	
	[Parameter(Mandatory=$false,Position=2)]
	[int]$delayInSeconds
)
	
	# Do the math to convert the delay given into milliseconds
	# and divide by the sleep value so that the correct delay
	# timer value can be set
	$sleep = 250
	$delay = ($delayInSeconds*1000)/$sleep
	$count = 0
	$charArray = New-Object System.Collections.ArrayList
	Write-host -nonewline "$($prompt):  "
	
	# While loop waits for the first key to be pressed for input and
	# then exits.  If the timer expires it returns null
	While ( (!$host.ui.rawui.KeyAvailable) -and ($count -lt $delay) ){
		start-sleep -m $sleep
		$count++
		If ($count -eq $delay) { "`n"; return $null}
	}
	
	# Retrieve the key pressed, add it to the char array that is storing
	# all keys pressed and then write it to the same line as the prompt
	$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp").Character
	$charArray.Add($key) | out-null
	Write-host -nonewline $key
	
	# This block is where the script keeps reading for a key.  Every time
	# a key is pressed, it checks if it's a carriage return.  If so, it exits the
	# loop and returns the string.  If not it stores the key pressed and
	# then checks if it's a backspace and does the necessary cursor 
	# moving and blanking out of the backspaced character, then resumes 
	# writing. 
	$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
	While ($key.virtualKeyCode -ne 13) {
		If ($key.virtualKeycode -eq 8) {
			$charArray.Add($key.Character) | out-null
			Write-host -nonewline $key.Character
			$cursor = $host.ui.rawui.get_cursorPosition()
			write-host -nonewline " "
			$host.ui.rawui.set_cursorPosition($cursor)
			$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
		}
		Else {
			$charArray.Add($key.Character) | out-null
			Write-host -nonewline $key.Character
			$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyUp")
		}
	}
	""
	$finalString = -join $charArray
	return $finalString
}

27 September 2014

Finally fixed the overlapping text in my comments sections, lol.  I sat down and prepared myself to dig in… but it turned out to be quite simple.  Utilizing the ever-helpfull Firebug plugin I pinned down the offending div and took out it’s fixed height.  I also needed to set a min height on another div since, I think, the floating avatar image was causing the CSS to not obey the margin set by the div it was nested inside.  Weird… but then, CSS has always been a point of confusion for me.  It’s definitely not intuitive!

What I should have done was written the comments code completely from scratch like I did the rest of the site…  Oh well, maybe one day!


25 September 2014

It’s funny how I can write code and then a long time afterwards need to write something for the same goal and come up with it in an easier way.  It makes sense, since I’ve likely learned more and grown in the time that has passed.  Still, to look back on the old cold and see how I fumbled through it or took a very long time to accomplish it and then do it in just a few lines makes me chuckle.

Take, for instance, NetStat.  Now, I know there are plenty of functions and cmdlets out there that other folks have written to get you “objectized” results for NetStat.  I’m sure they work well but I wouldn’t be who I am if I didn’t write my own code to do something.  Lately I’ve been guilty of using the cmdlets from Citrix and VMWare to interact with their items but for the sake of practicality, I have to.  Anyway… I’m troubleshooting slowness reports with one of our Citrix PVS environments but one of the problems is I don’t get told until hours afterwards and/or no one is willing to troubleshoot on the spot.  I can’t do anything if I can’t get data.  So, I needed to write a script that did not require a lot of resources and could run on the servers at the same time the users were using them at the heaviest time of the day – without impacting them.  At the time of this writing it hasn’t run for the first time yet out in the live environment, so after it does (and it’s successful *fingers crossed*) I may just create a post for the whole script.

Part of the script is collecting latency data.  I’m using a standard ping with the default packet size, doing an average of three pings and reporting back.  The piece that I wrote anew, despite having code already, was getting all of the connection data so that I could ping each connection and find latency.  Here’s what I do first:

$netstat = (netstat -ano) | where { ($_ -match "\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}") -and ($_ -match "TCP")  }

I’m pulling net stat and then only selecting the lines that 1) match a 4 octet IPv4 address and 2) are using TCP.  You can always further qualify it by adding a “-notmatch” and putting in any IP address you don’t want like local loopback stuff or all the 0.0.0.0.  I get something like this (I’m using just a sample of the data off a vm):

 TCP    127.0.0.1:49160        127.0.0.1:49161        ESTABLISHED     2232
 TCP    127.0.0.1:49161        127.0.0.1:49160        ESTABLISHED     2232
 TCP    127.0.0.1:49162        127.0.0.1:49163        ESTABLISHED     2232
 TCP    127.0.0.1:49163        127.0.0.1:49162        ESTABLISHED     2232
 TCP    127.0.0.1:49164        127.0.0.1:49165        ESTABLISHED     2232
 TCP    127.0.0.1:49165        127.0.0.1:49164        ESTABLISHED     2232
 TCP    127.0.0.1:49172        127.0.0.1:49173        ESTABLISHED     2232
 TCP    127.0.0.1:49173        127.0.0.1:49172        ESTABLISHED     2232
 TCP    127.0.0.1:49174        127.0.0.1:49175        ESTABLISHED     2232
 TCP    127.0.0.1:49175        127.0.0.1:49174        ESTABLISHED     2232
 TCP    127.0.0.1:49176        127.0.0.1:49177        ESTABLISHED     2232
 TCP    127.0.0.1:49177        127.0.0.1:49176        ESTABLISHED     2232
 TCP    127.0.0.1:49178        127.0.0.1:49179        ESTABLISHED     2232
 TCP    127.0.0.1:49179        127.0.0.1:49178        ESTABLISHED     2232
 TCP    127.0.0.1:49185        127.0.0.1:65001        ESTABLISHED     2060
 TCP    127.0.0.1:55460        127.0.0.1:27015        ESTABLISHED     11792
 TCP    127.0.0.1:55462        127.0.0.1:5354         ESTABLISHED     11792
 TCP    127.0.0.1:55466        127.0.0.1:5354         ESTABLISHED     11792
 TCP    127.0.0.1:55467        127.0.0.1:5354         ESTABLISHED     11792
 TCP    127.0.0.1:55469        127.0.0.1:55470        ESTABLISHED     13388

Then, to pull out the “Foreign” address (the remote machine’s IP) I do some splitting and replacing of each line to widdle it down to just the IP address:

ForEach ($rec in $netstat) {
	$foreignAddress = (($rec.split(" ") | where { $_ -match "\d{1,3}\." } )[1].Split(":"))[0]

As I go through each line (which is just a string of text) I first split it by spaces, which is the part of the code up to the first | (pipe) which ends up looking like this:

TCP



127.0.0.1:1120










127.0.0.1:59964













LISTENING






16932

It looks like a mess but it’s made it a lot simpler.  Now each non-blank string of text is on it’s own line, and thus, it has an index.  But, I can further widdle this down by only matching those items that begin with an octet.  That’s where I pipe part one above into the “where” clause and get:

127.0.0.1:1120
127.0.0.1:59964

Awesome.  So I know that the foreign address is always the second one (index “1”) but there’s also that port on there which will break the ping attempt so we need to take that off.  That’s where the rest of the code at the end of the “where” clause comes in.  I tell it I want index 1 ([1]), which is the line “127.0.0.1:59964” and then I split by the “:” which produces two lines, “127.0.0.1” and “59964”  I only want the IP and that is index “0” and so my line ends with a [0].  Note that each step I’ve explained above is in it’s own set of parenthesis.

I know I didn’t do the best job of explaining the breakdown but hopefully it has helped make sense of nesting commands with PowerShell and been an example of how powerful piping and one line of code can be.  My overall code here is 3 lines but it took a mess of a string and brought it down to the exact piece of data that I wanted.  I think that’s pretty darn cool.  For anyone wishing to take this a step further and make their own “objectized” version of NetStat or another text-output tool you should be able to go from here using this.  Maybe a quick google search on how to eliminate blank lines from a multi-line string, too.  I know I have that code somewhere….  If I were smart I’d do the same, that way I can reuse the function no matter which individual piece of data in the results I was looking for and not had to modify anything…


3 August 2014

I hate it when I find a new — and very easy — solution to a problem.  I’ve only recently discovered the parameter -ExecutionPolicy to the powershell.exe application and the value “Bypass.”  It allows you to, you guessed it, bypass whatever execution policy is set even if it’s restricted.  Non-admins can use it too… which at first seems really stupid but the bypass does not elevate anything so if they don’t have rights, they don’t have rights.

Anyway, that’s probably been around forever and I feel really stupid for just learning about it.  I discovered it when I had a script run at startup on a machine that didn’t have an execution policy set to something other than restricted and I saw the command line for the process using that parameter.

Even though I feel really stupid I’m glad I found it 🙂


20 April 2014

Some time ago I posted a script to remotely enable WinRM on a machine.  I have to admit it was fairly kludgy in that it had a lot of moving parts that had to execute just right.  Registry edits to turn on features, create listener config, modify firewall rules, and then cmdlets to remotely stop and start services.  I even wrote one that tried to rely on setting scheduled tasks with batch scripts (which would also call Powershell scripts) to get the job done…   There are a lot of points of failure in both attempts.

The scheduled tasks didn’t always execute correctly (or even get set to be executed) and I just had a lot of trouble with it.  That’s why I set out to write the second one.  The second one, while still a very inelegant way to do it, was solid except for one point of failure that, well, failed.  It failed almost all of the time because it required too perfect a condition.

Stopping and restarting the Windows Firewall Service remotely is just a bad idea.  If the timing between firing off the stop and start is perfect (i.e. lightning fast) then chances are the start command wont ever make it to the remote.  The reason is because once the Firewall Service is stopped the OS goes into a bit of a lockdown and doesn’t allow incoming communication via things like WMI.  It’s a great safety mechanism against remote attacks but a real pain in the butt when you don’t have time to (or just can’t) go to a machine.  The only other way to do it is locally… but I just said sometimes you can’t get to the local machine.  WinRM is our way to execute things remotely as if we were local… and if it’s not enabled then what is the solution?  There is another way to execute remote commands as if they were local!

Enter the Invoke-WMIMethod cmdlet.  This cmdlet allowed me to greatly improve my success rate and make it very elegant!  It simplifies the script  a great deal and only needs one remote mechanism to function.  My original function was 51 lines long, the second one 35, and now this one is only 18!  It can be reduced more but I have detection and logging in there too.  I’ve adapted it to be a standalone script for sharing here .  Without my monstrous heading (which I hope you’ll keep in tact if you use or share this…) it’s still only 30 lines (which includes several comments).  The one caveat to this script is that in order for it to work, Remote WMI rules in the firewall need to be enabled to allow it through!  I dont believe these are turned on by default (in my company we turn them on, on all PC’s so we can mange them…).

OK, I’ve rambled enough, here’s the script:  EnableWinRM

 

Thanks to this script for teaching me that the Invoke-WMIMethod was capable of starting a new process:  http://gallery.technet.microsoft.com/scriptcenter/Get-NetworkStatistics-66057d71

 


20 April 2014

I’ve learned that you need a Host-only network adapter for both VM’s in order for the workstation to connect to the server.  Thanks http://www.duncansutcliffe.com/?p=16


Links

RSS 2.0 Feed

Support

Brave Rewards
This site supports Brave Rewards. Please consider tipping or adding it to your monthly contributions if you find anything helpful!

For other ways: Support

Support this blog! If you have found it helpfu you can send me crypto. Any amount is appreciated!
ETH: 0xBEaF72807Cb5f4a8CCE23A5E7949041f62e8F8f0 | BTC: 3HTK5VJWr3vnftxbrcWAszLkRTrx9s5KzZ | SHIB: 0xdb209f96dD1BdcC12f03FdDfFAD0602276fb29BE
Brave Users you can send me BAT using the browser.