Tuesday, January 27, 2009

This is my third update to this script.  I think this script will finally push me into "programming like a real man" (e.g. with functions, params, hash arrays, throws, traps other optimizations) e.g. http://rmfdevelopment.com/PowerShell_Scripts/QueryEventFunction.ps1
Interestingly, I found System Event ID 36 breaks my script. It is a failure of the Windows Time Service and I think I have found a defect in Time/Date formatting:

## Takes Event Log queries...and finds elapsed time from event
# Default queries localhost system shutdown (EventID 6009)
# E.G. .\EventLogQueries.ps1 System 4072
# E.G .  6005..6009 | %{.\EventLogQueries.ps1 System $_ }    

if ($args[0] -eq $Null) {$Log_Type = "System"} else {$Log_Type = $args[0]}
if ($args[1] -eq $Null) {$Event_ID = 6009} else {$Event_ID = $args[1]}
write $args[1]
## TODO for remote and other properties:
## if ($args[3] = $Null) {$Computer = localhost} else {$args[3] = $Computer}

# query Event Log

$EventLog = get-eventlog -log $Log_Type | Select Message,EventID,TimeGenerated
$Event = $EventLog | ?{$_.eventID -eq $Event_ID}
$EventID = $Event | %{$_.eventID}
$Message = $Event | %{$_.Message}
$TimeGenerated = $Event | %{$_.TimeGenerated}
 
## TODO: Needs Trap or Throw for bad date or time format from Microsoft like for Event ID 36
## EventID 36 will break this script because....(??) 
## if EventID is null, discard query

if  ($LogType = "System" -and $EventID -eq 36) {$EventID = $NULL;write "Skip System EventID 36 because it breaks this script"}
if  ($EventID -ne $NULL)
{
    # Find elapsed time, total restarts, restarts/days and generate some arrays
    ## DBG::$test_EventID = $EventID[0]
    ## DBG::$test_Args_1  = $Args[1]
    ## DBG::write "Args[1]:$test_Args_1 -- Date/Time -- Elapsed Time (D.H.M.S)"
    
    write  "EventID -- Message -- Date/Time -- Elapsed Time (D.H.M.S)"
    $array_count = ($Event).count - 1
    $total_events = ($Event).count
    $curr_date = get-date
    $first_event_date = $TimeGenerated[$array_count]
    $last_event_date = $TimeGenerated[0]
    $event_time_span =($curr_date - $first_event_date)  
    $elapsed = $TimeGenerated[0..$array_count] | %{($curr_date - $_)}
    $AverageDaysBetweenEvents = $event_time_span.Days%$total_events
    
    
    ## Report Data
    ## What happens if data field is null?
    0..$array_count | %{
    $days = $elapsed[$_].days;
    $hours = $elapsed[$_].hours;
    $minutes = $elapsed[$_].minutes;
    $seconds = $elapsed[$_].seconds;
    $EventIDPrint = $EventID[$_];
    $MessagePrint = $Message[$_];
    $TimeGeneratedPrint = $TimeGenerated[$_];

    write "$EventIDPrint,$MessagePrint,$TimeGeneratedPrint,$days.$hours.$minutes.$seconds" }
    }
    
if  ($EventID -ne $NULL)
write "Number of Events:$total_events First Occurrence:$first_event_date Last Occurrence:$last_event_date Average Days Between Events:$AverageDaysBetweenEvents"
}


Friday, January 23, 2009

# Takes Event Log queries...and finds elapsed time from event
# E.G .  6005..6009 | %{.\EventLogQueries.ps1 System $_ }

if ($args[0] -eq $Null) {$Log_Type = "System"} else {$Log_Type = $args[0]}
if ($args[1] -eq $Null) {$Event_ID = 6009} else {$Event_ID = $args[1]}

# query Event Log
$EventLog = get-eventlog -log $Log_Type
$EventID = $EventLog | ?{$_.eventID -eq $Event_ID}

# If EventID is null, discard query
if  ($EventID -ne $NULL) 
{
    # Find Elapsed Time and Generate Array    
    write "Event ID -- Date/Time -- Elapsed Time (D.H.M.S)"
    $LogType_EventID_MsgProperty = $EventID | %{$_.TimeGenerated}
    $count = ($LogType_EventID_MsgProperty).count - 1
    $curr_date = get-date
    $array = $LogType_EventID_MsgProperty[0..$count] | %{($curr_date - $_)}
    
    # Report Data
    0..$count | %{
    $days = $array[$_].days;
    $hours = $array[$_].hours;
    $minutes = $array[$_].minutes;
    $seconds = $array[$_].seconds;
    $date = $LogType_EventID_MsgProperty[$_];
    write "$Event_ID -- $date -- $days.$hours.$minutes.$seconds";}
 } 

Output:
PS >6005..6009 | %{.\EventLogQueries.ps1 System $_ }
Event ID -- Date/Time -- Elapsed Time (D.H.M.S)
6005 -- 01/15/2009 22:40:17 -- 7.15.0.2
6005 -- 01/09/2009 09:52:51 -- 14.3.47.28
6005 -- 01/07/2009 17:39:39 -- 15.20.0.40
6005 -- 01/05/2009 18:30:06 -- 17.19.10.13
6005 -- 01/05/2009 11:01:59 -- 18.2.38.20
6005 -- 12/24/2008 11:20:21 -- 30.2.19.58
6005 -- 12/21/2008 10:01:15 -- 33.3.39.4
6005 -- 12/19/2008 09:23:52 -- 35.4.16.27
6005 -- 12/11/2008 08:04:59 -- 43.5.35.20
6005 -- 12/03/2008 08:10:23 -- 51.5.29.56
Event ID -- Date/Time -- Elapsed Time (D.H.M.S)
6006 -- 01/15/2009 09:28:14 -- 8.4.12.6
6006 -- 01/09/2009 09:52:04 -- 14.3.48.16
6006 -- 01/07/2009 17:38:05 -- 15.20.2.15
6006 -- 01/05/2009 11:00:36 -- 18.2.39.44
6006 -- 12/19/2008 09:22:42 -- 35.4.17.38
6006 -- 12/11/2008 08:03:46 -- 43.5.36.34
Event ID -- Date/Time -- Elapsed Time (D.H.M.S)
6009 -- 01/15/2009 22:40:17 -- 7.15.0.5
6009 -- 01/09/2009 09:52:51 -- 14.3.47.31
6009 -- 01/07/2009 17:39:39 -- 15.20.0.43
6009 -- 01/05/2009 18:30:06 -- 17.19.10.16
6009 -- 01/05/2009 11:01:59 -- 18.2.38.23
6009 -- 12/24/2008 11:20:21 -- 30.2.20.1
6009 -- 12/21/2008 10:01:15 -- 33.3.39.7
6009 -- 12/19/2008 09:23:52 -- 35.4.16.30
6009 -- 12/11/2008 08:04:59 -- 43.5.35.23
6009 -- 12/03/2008 08:10:23 -- 51.5.29.59

Thursday, January 22, 2009

Last Boot Times.  A Microsoft.public.windows.powershell forum question made me think about how important historical knowledge of last boot times are to most administrators. Microsoft has an excellent utility (uptime.exe at http://support.microsoft.com/kb/232243) that records significant system events and estimates system availability. I wanted to see how difficult such a utility would be to recreate in Powershell. I was surprised to find some reporting differeneces for last uptime between the different approaches. To be continued...

##. \LastBootWMI.ps1
$colItems = Gwmi Win32_OperatingSystem -Namespace "root\CIMV2" -ComputerName localhost
$LB = $ColItems.ConvertToDateTime($ColItems.LastBootUpTime)
$array = (get-date).subtract($LB)
$Curr_date = (get-date) 
$days = $array.days
$hours = $array.hours
$minutes = $array.minutes
$seconds = $array.seconds
write "Last Boot Time: $LB Current Time: $Curr_date" 
write "Time from Last Boot in Days.Hours.Minutes.Seconds = $days.$hours.$minutes.$seconds"

PS >.\LastBootWMI.ps1
Last Boot Time: 01/20/2009 03:42:31 Current Time: 01/22/2009 19:00:25
Time from Last Boot in Days.Hours.Minutes.Seconds = 2.15.17.53


## .\LastBootTimes.ps1
$SysEvtLog = get-eventlog -log System
$Evt_ID_6009 = $SysEvtLog | ?{$_.eventID -eq 6009}
$Evt_ID_6009TG = $Evt_ID_6009 | %{$_.TimeGenerated}
$count = ($Evt_ID_6009TG.count) -1
$curr_date = get-date
$array = $Evt_ID_6009TG[0..$count] | %{($curr_date - $_)}
write "Last boot Date/Time -- LBTs from Current Time in Days.Hours.Minutes.Seconds"
0..$count | %{
$days = $array[$_].days;
$hours = $array[$_].hours;
$minutes = $array[$_].minutes;
$seconds = $array[$_].seconds;
$date = $Evt_ID_6009TG[$_];
write "$date -- $days.$hours.$minutes.$seconds";}

PS >.\LastBootTimes.ps1
Last boot Date/Time -- LBTs from Current Time in Days.Hours.Minutes.Seconds
01/15/2009 22:40:17 -- 6.20.20.5
01/09/2009 09:52:51 -- 13.9.7.31
01/07/2009 17:39:39 -- 15.1.20.43
01/05/2009 18:30:06 -- 17.0.30.16
01/05/2009 11:01:59 -- 17.7.58.23
12/24/2008 11:20:21 -- 29.7.40.1
12/21/2008 10:01:15 -- 32.8.59.7
12/19/2008 09:23:52 -- 34.9.36.30
12/11/2008 08:04:59 -- 42.10.55.23
12/03/2008 08:10:23 -- 50.10.49.59


D:\>uptime.exe /s
Uptime Report for: \\RMFMEDIA

Current OS: Microsoft Windows XP, Service Pack 3, Multiprocessor Free.
Time Zone: Pacific Standard Time

System Events as of 1/22/2009 7:13:12 PM:

Date:      Time:        Event:               Comment:
---------- -----------  -------------------  -----------------------------------
 12/3/2008  8:10:23 AM  Boot
12/11/2008  8:03:46 AM  Shutdown             Prior uptime:7d 23h:53m:23s
12/11/2008  8:04:59 AM  Boot                 Prior downtime:0d 0h:1m:13s
12/19/2008  9:22:42 AM  Shutdown             Prior uptime:8d 1h:17m:43s
12/19/2008  9:23:52 AM  Boot                 Prior downtime:0d 0h:1m:10s
12/21/2008 10:01:15 AM  Boot
12/24/2008 11:20:21 AM  Boot
  1/5/2009 11:00:36 AM  Shutdown             Prior uptime:11d 23h:40m:15s
  1/5/2009 11:01:59 AM  Boot                 Prior downtime:0d 0h:1m:23s
  1/5/2009  6:30:06 PM  Boot
  1/5/2009  6:30:12 PM  Bluescreen           STOP 0x0000008e
  1/7/2009  5:38:05 PM  Shutdown
  1/7/2009  5:39:39 PM  Boot                 Prior downtime:0d 0h:1m:34s
  1/9/2009  9:52:04 AM  Shutdown             Prior uptime:1d 16h:12m:25s
  1/9/2009  9:52:51 AM  Boot                 Prior downtime:0d 0h:0m:47s
 1/15/2009  9:28:14 AM  Shutdown             Prior uptime:5d 23h:35m:23s
 1/15/2009 10:40:17 PM  Boot                 Prior downtime:0d 13h:12m:3s

Current System Uptime: 6 day(s), 20 hour(s), 33 minute(s), 32 second(s)

Monday, January 19, 2009

Some notes on cmd line editing for Powershell in v2 CTP 3. It is often nice to be able to use a command line editor like Vim or Edlin when writing simple scripts. This allows the administrator to stay in one shell and one environment. Edlin allows the user to see the command history above and the editing space below. Bruce Payette's work lead me to create this function that replaces the venerable 'copy con' in DOS: 
function copy_con {[console]::In.ReadToEnd()}

My 'copy_con' function can be used to write to file or  variable:
copy_con > a.txt 
or
$a = copy_con

The same functionality is achieved in v2 CTP3 with read-host.

The 'out-gridview' cmdlet in CTP3 allows you to combine multiple output in one grid; a more readable and searchable window for multiple help files:  $a | %{help $_ -detailed} | out-gridview

Using the 'history' to create simple scripts is easy: 
$test_out = 25..30 | history| %{$_.CommandLine}

To execute the collection of commands from a variable:
$test_out  | %{Invoke-expression $_}


Monday, January 12, 2009

Parsing Logs again....Following up from a few posts ago. I would like to figure out how to get multiple log types working together with the simplest syntax in Powershell.  I think the goal would be to take events that happen from whatever handlers (Event Logs, Application Verifier, Windbg, NetMonitor, Syslogd, Firewall, IDS) and parse them into "congruent datetime stamped events" as objects (??). To get today's dump from Windows Firewall Audit (set this up in auditing...) messaging out of my Security Event Log, I do something like this:

$now = [System.DateTime]::get_now()
$NowSDS = $now.ToShortDateString()
$SEL = get-eventlog -logname Security | where-object {($_.timegenerated -match "$NowSDS") -and ($_.message -match "Windows Firewall")}  | fl * 
$SEL | out-file $pwd\SEL.txt
Select-string SEL.txt -pattern "Process Identifier","Path","Port number" -allmatches
## or 
Select-string SEL.txt -pattern "Process Identifier","Path","Port number" -allmatches | out-file SEL_SR.txt
$sr = [System.IO.StreamReader]("$pwd\SEL_SR.txt")
$sr.readToEnd()

Results are like:

...
SEL.txt:11669:                     Path: C:\WINDOWS\system32\svchost.exe
SEL.txt:11671:                     Process identifier: 1588
SEL.txt:11685:                     Port number: 50386
SEL.txt:11712:                     Path: C:\WINDOWS\system32\svchost.exe
SEL.txt:11714:                     Process identifier: 1588
SEL.txt:11728:                     Port number: 59453

...

Ungainly. Not parsing an object in the end but eventually text! So here we are parsing the Message descriptions from the Security Event log MSG field (which is text!) into an object:

$now = [System.DateTime]::get_now()
$NowSDS = $now.ToShortDateString()
$SEL = get-eventlog -logname Security | where-object {($_.timegenerated -match "$NowSDS") -and ($_.message -match "Windows Firewall")} 
$SEL_MSG =  $SEL | %{$_.message}
Select-string -inputobject $SEL_MSG -pattern "Process Identifier","Path","Port number" -allmatches

Okay, an object but not what I want yet....And Select-String isn't helping any here:

Name: -
Path: C:\WINDOWS\system32\svchost.exe
Process identifier: 1588
User account: NETWORK SERVICE
User domain: NT AUTHORITY
Service: Yes
RPC server: No
IP version: IPv4
IP protocol: UDP
Port number: 55033
Allowed: No
User notified: No The Windows Firewall has detected an application listening for incoming traffic.

## This doesn't work
## $SR = [System.IO.StreamReader]($SEL)
## $sr.readToEnd()

to be continued....

Monday, January 5, 2009

I am happy to see that the new ISE can open multiple buffers like GVIM7.2:

http://picasaweb.google.com/rferrisx/Powershell?authkey=Q5B4cx4qTOg#5287862514382137954

Friday, January 2, 2009

Searching for malware with Powershell



hmmm.....
This gives me output like:
1/3/2009 11:09 AM,19:9:16:859,100,csrss,1124,16388096,40923136,104357888,15458304,39809024,87851008
which covers WorkingSet, PrivateMemorySize, VirtualMemorySize, and their deltas between measurement interval.
## Use ps to measure Application Memory Deltas
## Run '.\ws_diff [Interval in Seconds] [Process Name] or
## to log all processes continually every 10 seconds -- 'while (1) {.\WS_diff.ps1 10 cmd >> ps_out.txt }'

# Create args as Variables or Objects
$sleep_time = $args[0]

# Create or define PS_Array. Default is ps is called without args. 
# Then take measurements $now, $then, $count
if ($args[1] -eq $NULL )
    {
    $then = ps | %{$_ | Select Name,ID,WorkingSet,PrivateMemorySize,VirtualMemorySize}
    sleep -seconds $sleep_time
    $now  = ps | %{$_ | Select Name,ID,WorkingSet,PrivateMemorySize,VirtualMemorySize}
    $count = ($now | Select Name).count
    }
else
    {
    $ps_array = ( ps $args[1] )
    $then = ps -inputobject $ps_array | %{$_ | Select Name,ID,WorkingSet,PrivateMemorySize,VirtualMemorySize}
    sleep -seconds $sleep_time
    $now  = ps -inputobject $ps_array | %{$_ | Select Name,ID,WorkingSet,PrivateMemorySize,VirtualMemorySize}
    $count = ($now | Select Name).count
    }

# Declare Time Measurements 
$date = (get-date -format g)
$hour = [DateTime]::UtcNow.TimeOfDay.Hours
$minutes = [DateTime]::UtcNow.TimeOfDay.Minutes
$seconds = [DateTime]::UtcNow.TimeOfDay.Seconds
$ms = [DateTime]::UtcNow.TimeOfDay.Milliseconds

# Write output and find diffs. Check if process has multiple instances first
if ( $count -gt 1 ) 
{
    $array_out = 0..$count |
    %{ 
    $date + "," +
    $hour + ":" + $minutes + ":" + $seconds + ":"  + $ms + "," +
    $sleep_time + "," + $now[$_].Name + "," + $now[$_].ID + "," +
    $now[$_].WorkingSet + "," +
    $now[$_].PrivateMemorySize + "," +
    $now[$_].VirtualMemorySize + "," +
    ( ($now[$_].WorkingSet) - ($then[$_].WorkingSet) ) + "," +
    ( ($now[$_].PrivateMemorySize) - ($then[$_].PrivateMemorySize) ) + "," +
    ( ($now[$_].VirtualMemorySize) - ($then[$_].VirtualMemorySize) )
    }
}

else  
{
    $array_out =
    $date + "," +
    $hour + ":" + $minutes + ":" + $seconds + ":"  + $ms + "," +
    $sleep_time + "," + $now.Name + "," + $now.ID + "," +
    $now.WorkingSet + "," +
    $now.PrivateMemorySize + "," +
    $now.VirtualMemorySize + "," +
    ( ($now.WorkingSet) - ($then.WorkingSet) ) + "," +
    ( ($now.PrivateMemorySize) - ($then.PrivateMemorySize) ) + "," +
    ( ($now.VirtualMemorySize) - ($then.VirtualMemorySize) )
}

write $array_out