PowerShell: select string in standard output? - powershell

PS C:\squid\sbin> .\squid.exe -v
Squid Cache: Version 2.7.STABLE8
configure options: --enable-win32-service --enable-storeio='ufs aufs null coss' --enable-default-hostsfile=none --enable
-removal-policies='heap lru' --enable-snmp --enable-htcp --disable-wccp --disable-wccpv2 --enable-useragent-log --enable
-referer-log --enable-cache-digests --enable-auth='basic ntlm digest negotiate' --enable-basic-auth-helpers='LDAP NCSA m
swin_sspi squid_radius_auth' --enable-negotiate-auth-helpers=mswin_sspi --enable-ntlm-auth-helpers='mswin_sspi fakeauth'
--enable-external-acl-helpers='mswin_ad_group mswin_lm_group ldap_group' --enable-large-cache-files --enable-digest-aut
h-helpers='password LDAP eDirectory' --enable-forw-via-db --enable-follow-x-forwarded-for --enable-arp-acl --prefix=c:/s
quid
Compiled as Windows System Service.
PS C:\squid\sbin> .\squid.exe -v|Select-String Squid
squid.exe -v will output its version information, which contains keyword "Squid".
I want powershell to tell me whether keyword "Squid" exists in the output. So I use .\squid.exe -v|Select-String Squid, but it outputs nothing.
What's the right way to do it? I'm using PS 3.0.

You ARE doing it the right way :)
The problem is not your code but the squid port itself. Its doing something weird to write text to the console to where PowerShell and cmd can't capture it through the stdout/stderr streams. I'm guessing instead of using the stdout/stderr api it may be manipulating characters on the console directly or something. I tried redirecting stderr to stdout (2>&1) but that didn't work either.
It comes with a change log text file, I guess you can just parse that instead...
EDIT --
Or you can use this kludgy but serviceable workaround to scrape the console text:
function Get-ConsoleText {
if ($host.Name -eq 'ConsoleHost') {
$text_builder = new-object system.text.stringbuilder
$buffer_width = $host.ui.rawui.BufferSize.Width
$buffer_height = $host.ui.rawui.CursorPosition.Y
$rec = new-object System.Management.Automation.Host.Rectangle 0,0,($buffer_width -2), $buffer_height
$buffer = $host.ui.rawui.GetBufferContents($rec)
$console_out = #()
for($i = 0; $i -lt $buffer_height; $i++) {
$text_builder = new-object system.text.stringbuilder
for($j = 0; $j -lt $buffer_width; $j++) {
$cell = $buffer[$i,$j]
$text_builder.Append($cell.Character) | Out-Null
}
$console_out += $text_builder.ToString()
}
return $console_out
}
}
cls; .\squid.exe -v; Get-ConsoleText |
ForEach-Object {
if ($_ -match 'Version (.+)') {$matches[1]}
}

Related

Is this code a Keylogger? What does it do?

Due to Windows10 task manager I have a powershell.exe running which is continously consuming 8% CPU and blocking 64MB of RAM. After inspecting my Windows event log I found a pipeline event (800) with subsequent code:
Add-Type -AssemblyName System.Core
function Run-Server() {
param([string]$h);
$b = New-Object byte[] 8;
$p = New-Object System.IO.Pipes.AnonymousPipeClientStream -ArgumentList #([System.IO.Pipes.PipeDirection]::In, $h);
if ($p) {
$l = $p.Read($b, 0, 8); while ($l -gt 7) {
$c = [System.BitConverter]::ToInt32($b, 0); $l = System.BitConverter]::ToInt32($b, 4);
$t = $null; if ($l -gt 0) {
$t1 = New-Object byte[] $l;
$l = $p.Read($t1, 0, $t1.Length);
$t = [System.Text.Encoding]::UTF8.GetString($t1, 0, $l) }
if ($c -eq 1) { Invoke-Expression $t } elseif ($c -eq 9) { break } $l = $p.Read($b, 0, 8) }
$p.Dispose()
}
} Run-Server -h 728
I'm working in a corporate environment and I'm not a Powershell expert, but it seems as the script is catching byte by byte and make a string out of it? Do you have any idea what this script could be used for? Do you think it can cause the given indication of 8% CPU and 64MB RAM usage?
I formatted the code, changed the variable names and added some comments to make it easier to understand:
Add-Type -AssemblyName System.Core
function Run-Server() {
param(
[string]$h
);
$buffer = New-Object byte[] 8;
# Creates an annonymous pipe
$pipe = New-Object System.IO.Pipes.AnonymousPipeClientStream -ArgumentList #([System.IO.Pipes.PipeDirection]::In, $h);
if ($pipe) {
# Read up to 8 bytes from the pipe
$readBytes = $pipe.Read($buffer,0, 8); #(byte[] buffer, int offset, int count);
# if it managed to read 8 bytes
while ($readBytes -gt 7) {
# Seems the sender is sending some kind of 'command' or instruction.
# If command is '1' means execute the rest as a script
# If command is '9' means terminate
$command = [System.BitConverter]::ToInt32($buffer,0);
# Seems that in position 4 it sends how big the text will be
$textSize = [System.BitConverter]::ToInt32($buffer,4); # ToInt32 (byte[] value, int startIndex);
# based on the $textSize, read the full message and convert it to string ($text)
$text = $null;
if ($readBytes -gt 0) {
$text1 = New-Object byte[] $textSize;
$readBytes = $pipe.Read($text1, 0, $text1.Length);
$text = [System.Text.Encoding]::UTF8.GetString($text1, 0, $readBytes)
}
if ($command -eq 1) {
# Scary! execute the text string that came from the pipe
Invoke-Expression $text
}
elseif ($command -eq 9) {
break
}
$readBytes = $pipe.Read($buffer,0, 8)
}
$pipe.Dispose()
}
}
Run-Server -h 728
Infor about pipe: AnonymousPipeClientStream Class
That codes creates an In pipe with handle 728 and receives a script from another process, then it executes the script
Some details:
The first message seems to be a kind of command ($c) and an indication of how big the script will be ($l)
Then it reads a second message of size ($l) and, if command == 1, it executes the second message as if it would be a powershell script: Invoke-Expression $t (scary!)
Folks, I'm from Snow Software and can confirm that this is a legit code executed by Snow Inventory Agent to run PowerShell scripts that are deployed with the agents for gathering more advanced information about the device and certain apps installed on it. It does indeed run the anonymous pipe and send the Powershell code as text sourced from the encrypted script files that are deployed together with the agent. The gathered data is used by Snow Software and Technology Asset Management product suite and is deployed by large organizations to optimize technology spend, get visibility, and manageability of the technology assets.
Let me know if you have more questions!
I happened to run into the same issue. After some digging through my system (grep), I found
out that the offending code occurs in an executable 'snowagent.exe'. As far as I can tell it is used by our (company) IT department to get an inventory of the applications installed on my machine, and maybe more.
As such, I conclude that it is at least not a big issue (virus/malware). Still, if I am
hampered by it (i.e. eating away 13% CPU), I just kill it.
gr M.

Unable to iterate through e-mail objects using powershell in Outlook

I'm writing a script, which takes Outlook folder as input and moves every unread mail to different folder. My code:
Add-Type -assembly "Microsoft.Office.Interop.Outlook"
$Outlook = New-Object -ComObject Outlook.Application
$namespace = $Outlook.GetNameSpace("MAPI")
$olFolderInbox = 6
$inbox = $namespace.GetDefaultFolder($olFolderInbox)
$myFolder = $namespace.pickfolder()
$toFolder = $inbox.Folders | where-object { $_.name -eq "UnreadMessages" }
$messages = $myFolder.Items
$messageCount = $messages.count
for ($i = $messageCount - 1; $i -ge 0; $i--)
{
if ($messages[$i].unread -eq $True)
{
$message.move($toFolder)
}
}
The problem is, that I can not iterate through "messages" objects. Error:
Unable to index into an object of type System.__ComObject.
If it's not possible, then how am I supposed to do it?
Thanks in advance.
EDIT. It's my first day using powershell :)
All Outlook collections are 1 based, not 0 - you need to iterate from Items.Count down to 1.
Secondly, do not just iterate through all messages in a folder, use Items.Find/FindNext. In your case, the search criteria would be "[Unread] = true".
Try to do a get-member on it, and see what your options are.
$Messages | Get-Member -force
If you figure out how you want to proceed, then you can use the ForEach-Object to loop through $Messages' content like this
$Messages | ForEach-Object { Script code }
Or use its abbreviation:
$Messages | % { Script code }
While you cannot use PowerShell's usual indexing syntax - $messages[$i] -
for accessing the elements of $messages, you can use .Items($i) on the folder object (see the docs):
Additionally, as noted in Dmitry Streblechenko's helpful answer, indices start at 1, not 0:
for ($i = $myFolder.Items.Count; $i -ge 1; $i--)
{
$message = $myFolder.Items($i)
if ($message.unread)
{
$message.move($toFolder)
}
}
For simple forward enumeration without indices you can use foreach:
foreach ($message in $myFolder.Items) {
if ($message.unread)
{
$message.move($toFolder)
}
}
($myFolder.Items | ForEach-Object { ... } should work too, but it will be slower.)
That said, Dmitry's answer answer also points to how to perform a filtered enumeration at the source, which performs much better.
Thanks for replying. All your answers were helpful. I finally used foreach loop. While I was trying to use $message = $myFolder.Items($i) following error appeared:
Method invocation failed because [System.__ComObject] doesn't cont
ain a method named 'Items'
so there is no possibility to iterate using indexes in this case.
Thank You all for your time :)

Simulating `ls` in Powershell

I'm trying to get something that looks like UNIX ls output in PowerShell. This is getting there:
Get-ChildItem | Format-Wide -AutoSize -Property Name
but it's still outputting the items in row-major instead of column-major order:
PS C:\Users\Mark Reed> Get-ChildItem | Format-Wide -AutoSize -Property Name
Contacts Desktop Documents Downloads Favorites
Links Music Pictures Saved Games
Searches Videos
Desired output:
PS C:\Users\Mark Reed> My-List-Files
Contacts Downloads Music Searches
Desktop Favorites Pictures Videos
Documents Links Saved Games
The difference is in the sorting: 1 2 3 4 5/6 7 8 9 reading across the lines, vs 1/2/3 4/5/6 7/8/9 reading down the columns.
I already have a script that will take an array and print it out in column-major order using Write-Host, though I found a lot of PowerShellish idiomatic improvements to it by reading Keith's and Roman's takes. But my impression from reading around is that's the wrong way to go about this. Instead of calling Write-Host, a script should output objects, and let the formatters and outputters take care of getting the right stuff written to the user's console.
When a script uses Write-Host, its output is not capturable; if I assign the result to a variable, I get a null variable and the output is written to the screen anyway. It's like a command in the middle of a UNIX pipeline writing directly to /dev/tty instead of standard output or even standard error.
Admittedly, I may not be able to do much with the array of Microsoft.PowerShell.Commands.Internal.Format.* objects I get back from e.g. Format-Wide, but at least it contains the output, which doesn't show up on my screen in rogue fashion, and which I can recreate at any time by passing the array to another formatter or outputter.
This is a simple-ish function that formats column major. You can do this all in PowerShell Script:
function Format-WideColMajor {
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline)]
[AllowNull()]
[AllowEmptyString()]
[PSObject]
$InputObject,
[Parameter()]
$Property
)
begin {
$list = new-object System.Collections.Generic.List[PSObject]
}
process {
$list.Add($InputObject)
}
end {
if ($Property) {
$output = $list | Foreach {"$($_.$Property)"}
}
else {
$output = $list | Foreach {"$_"}
}
$conWidth = $Host.UI.RawUI.BufferSize.Width - 1
$maxLen = ($output | Measure-Object -Property Length -Maximum).Maximum
$colWidth = $maxLen + 1
$numCols = [Math]::Floor($conWidth / $colWidth)
$numRows = [Math]::Ceiling($output.Count / $numCols)
for ($i=0; $i -lt $numRows; $i++) {
$line = ""
for ($j = 0; $j -lt $numCols; $j++) {
$item = $output[$i + ($j * $numRows)]
$line += "$item$(' ' * ($colWidth - $item.Length))"
}
$line
}
}
}

Powershell get repadmin /istg in array?

Is this possible?
I'm brand new to powershell and am currently in the process of converting a vbscript script to Powershell. The following one-liner command seems to do exactly what the entire vbscript does:
Repadmin /istg
which outputs
Repadmin: running command /istg against full DC ST-DC7.somestuff.com
Gathering topology from site BR-CORP (ST-DC7.somestuff.com):
Site ISTG
================== =================
Portland ST-DC4
Venyu ST-DC5
BR-Office ST-DC3
BR-CORP ST-DC7
The problem is I need to return this info (namely the last 4 lines) as objects which contain a "Site" and "ISTG" field. I tried the following:
$returnValues = Repadmin /istg
$returnValues
But this didin't return anything (possibly because Repadmin writes out the lines instead of actually returning the data?)
Is there a way to get the Info from "Repadmin /istg" into an array?
Here's one possible way, using regular expressions:
$output = repadmin /istg
for ( $n = 10; $n -lt $output.Count; $n++ ) {
if ( $output[$n] -ne "" ) {
$output[$n] | select-string '\s*(\S*)\s*(\S*)$' | foreach-object {
$site = $_.Matches[0].Groups[1].Value
$istg = $_.Matches[0].Groups[2].Value
}
new-object PSObject -property #{
"Site" = $site
"ISTG" = $istg
} | select-object Site,ISTG
}
}
You have to start parsing the 10th item of output and ignore empty lines because repadmin.exe seems to insert superflous line breaks (or at least, PowerShell thinks so).

PowerShell: reading PowerShell Transcript logs

I've started to use the start-transcript in my profile to keep a log of everything I do via the shell.
it's becoming useful for looking back at what changes are made and when they were made. I'm also beginning to use it as the first steps of documentation. I've been commenting the things done in the shell for future reference.
The thing that is proving tricky is the formatting is that of a text doc and is not as easy to read as the shell (error, verbose and warning colours mainly).
I was wondering if anybody uses the Transcript functionality in this way and has a viewer of preference or a script that parses the log file to produce a doc of some sort?
Edit: i'm interested to know why the question has been down voted...
I believe it will be very hard to parse a transcript to create an accurate formatted document. You could however use the console host API to capture (parts of) the screen buffer.
This Windows Powershell blog article describes how this works.
A trivial way to use the (modified) script (Get-ConsoleAsHtml.ps1) is to modify your prompt function, so that all lines from the buffer that haven't been written to your html transcript yet, are saved every time the prompt function is called. The first block of code is the contents of the modified script, the second block of code shows how you can use this script in your profile.
###########################################################################################################
# Get-ConsoleAsHtml.ps1
#
# The script captures console screen buffer up to the current cursor position and returns it in HTML format.
# (Jon Z: Added a startline parameter)
#
# Returns: UTF8-encoded string.
#
# Example:
#
# $htmlFileName = "$env:temp\ConsoleBuffer.html"
# .\Get-ConsoleAsHtml 5 | out-file $htmlFileName -encoding UTF8
# $null = [System.Diagnostics.Process]::Start("$htmlFileName")
#
param (
$startline = 0
)
# Check the host name and exit if the host is not the Windows PowerShell console host.
if ($host.Name -ne 'ConsoleHost')
{
write-host -ForegroundColor Red "This script runs only in the console host. You cannot run this script in $($host.Name)."
exit -1
}
# The Windows PowerShell console host redefines DarkYellow and DarkMagenta colors and uses them as defaults.
# The redefined colors do not correspond to the color names used in HTML, so they need to be mapped to digital color codes.
#
function Normalize-HtmlColor ($color)
{
if ($color -eq "DarkYellow") { $color = "#eeedf0" }
if ($color -eq "DarkMagenta") { $color = "#012456" }
return $color
}
# Create an HTML span from text using the named console colors.
#
function Make-HtmlSpan ($text, $forecolor = "DarkYellow", $backcolor = "DarkMagenta")
{
$forecolor = Normalize-HtmlColor $forecolor
$backcolor = Normalize-HtmlColor $backcolor
# You can also add font-weight:bold tag here if you want a bold font in output.
return "<span style='font-family:Courier New;color:$forecolor;background:$backcolor'>$text</span>"
}
# Generate an HTML span and append it to HTML string builder
#
function Append-HtmlSpan
{
$spanText = $spanBuilder.ToString()
$spanHtml = Make-HtmlSpan $spanText $currentForegroundColor $currentBackgroundColor
$null = $htmlBuilder.Append($spanHtml)
}
# Append line break to HTML builder
#
function Append-HtmlBreak
{
$null = $htmlBuilder.Append("<br>")
}
# Initialize the HTML string builder.
$htmlBuilder = new-object system.text.stringbuilder
$null = $htmlBuilder.Append("<pre style='MARGIN: 0in 10pt 0in;line-height:normal';font-size:10pt>")
# Grab the console screen buffer contents using the Host console API.
$bufferWidth = $host.ui.rawui.BufferSize.Width
$bufferHeight = $host.ui.rawui.CursorPosition.Y
$rec = new-object System.Management.Automation.Host.Rectangle 0,0,($bufferWidth - 1),$bufferHeight
$buffer = $host.ui.rawui.GetBufferContents($rec)
# Iterate through the lines in the console buffer.
for($i = $startline; $i -lt $bufferHeight; $i++)
{
$spanBuilder = new-object system.text.stringbuilder
# Track the colors to identify spans of text with the same formatting.
$currentForegroundColor = $buffer[$i, 0].Foregroundcolor
$currentBackgroundColor = $buffer[$i, 0].Backgroundcolor
for($j = 0; $j -lt $bufferWidth; $j++)
{
$cell = $buffer[$i,$j]
# If the colors change, generate an HTML span and append it to the HTML string builder.
if (($cell.ForegroundColor -ne $currentForegroundColor) -or ($cell.BackgroundColor -ne $currentBackgroundColor))
{
Append-HtmlSpan
# Reset the span builder and colors.
$spanBuilder = new-object system.text.stringbuilder
$currentForegroundColor = $cell.Foregroundcolor
$currentBackgroundColor = $cell.Backgroundcolor
}
# Substitute characters which have special meaning in HTML.
switch ($cell.Character)
{
'>' { $htmlChar = '>' }
'<' { $htmlChar = '<' }
'&' { $htmlChar = '&' }
default
{
$htmlChar = $cell.Character
}
}
$null = $spanBuilder.Append($htmlChar)
}
Append-HtmlSpan
Append-HtmlBreak
}
# Append HTML ending tag.
$null = $htmlBuilder.Append("</pre>")
return $htmlBuilder.ToString()
Example of a profile:
############################################################################################################
# Microsoft.PowerShell_profile.ps1
#
$docpath = [environment]::GetFolderPath([environment+SpecialFolder]::MyDocuments)
$transcript = "$($docpath)\PowerShell_transcript.$(get-date -f 'yyyyMMddHHmmss').html";
$global:lastloggedline = 0
function prompt {
&'D:\Scripts\Get-ConsoleAsHtml.ps1' $global:lastloggedline | out-file $transcript -append;
$global:lastloggedline = $host.ui.rawui.cursorposition.Y
"PS $pwd$('>' * ($nestedPromptLevel + 1)) "
}