I've 2 questions regarding the code below :
Question 1:
For some unknown reason, the following code does not retrieve information from certain processes.
However, he manages to recover the sha256
Could you please explain why this behavior and how to fix address this behavior ?
Path is Null
WmiPrvSE
- [(PID=5356) () Description=]
-- Path="None (SHA256=AD938C303F12EA8D164433CC7BA46FC7B9AE00F6F899E308D4317DAB46E25642)
-----
Get-FileHash : Impossible de lier l'argument au paramètre « Path », car il a la valeur Null.
Au caractère C:\Users\LEFBE\Desktop\Klic-20-12-2021\Process.ps1:4 : 33
+ $FileHash = (Get-FileHash -Path $_.Path -Algorithm SHA256 -ErrorActio ...
+ ~~~~~~~
+ CategoryInfo : InvalidData : (:) [Get-FileHash], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Get-FileHash
Question 2:
I'm having trouble retrieving the command line launched by the process. Could you please help me to get this information?
The code :
get-process | ForEach-Object {
$Name = $_.Name
$Desc = $_.Description
$FileHash = (Get-FileHash -Path $_.Path -Algorithm SHA256 -ErrorAction SilentlyContinue).hash
$ID = $_.ID
$Path = $_.Path
$CP = $_.Company
$CL = $_.Commandline
if ($Path -eq $null)
{
Write-Host "Path is Null"
$Path = "None"
Write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
}
else
{
Write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
}
}
Update 23/12/2021
The updated code works better than the first. (manage Null $Path value)
On the other hand, no command line can be obtained yet.
get-process | ForEach-Object {
$Name = $_.Name
$Desc = $_.Description
$ID = $_.ID
$Path = $_.Path
$CP = $_.Company
$CL = $_.Commandline
IF([string]::IsNullOrEmpty($Path)) {
$Path = "None"
$FileHash = "None"
write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
} else {
$FileHash = (Get-FileHash -LiteralPath $_.Path -Algorithm SHA256 -ErrorAction SilentlyContinue).hash
Write-Host "$Name"
Write-Host "- [(PID=$ID) ($CP) Description=$Desc]"
Write-Host "-- Path=""$Path (SHA256=$FileHash)"
Write-Host $CL
Write-Host "-----"
}
}
Thanks for your help,
LEFBE
You can use a calculated property with Select-Object to generate your new object. As for "having trouble retrieving the command line", I'm assuming you're unable to capture the output of your script with Out-File or similar, this is because Write-Host sends the output to the Information Stream and it's output can't be captured unless redirected (6>&1).
Get-Process | Select-Object Name, Description, #{
Name = 'Hash'
Expression = { (Get-FileHash $_.Path -Algorithm SHA256).Hash }
}, ID, Path, Company, CommandLine
Related
I'm new to scripting any I am trying to make a script that reads PC list from a CSV file and check if a specific process is runnuing.
the following code is:
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $($(Get-Date -Format yyyyMMdd-HHmmss) + " - " + $logstring)
}
$LogFile = "Path\to\log.txt" # Location of local log file name
$CsvLocation = "\\path\to\CSV\File\Table.csv" #Location of the CSV file
$csvdata = Import-Csv $ipmCsvLocation
foreach($pc in $csvdata) {
$IcingaProc = "nscp.exe"
$pcname = $pc.Name
Write-Host $("Looking for process "+ $IcingaProc + " in " + $pcname + "...")
$Processes = get-process | Where-Object {$_.ProcessName -Like "nscp*"}
foreach($Proc in $Processes){
if ($Proc.ProcessName -eq $IcingaProc){
Write-Host "Program installed succefully"
LogWrite ("Program installed succefully in "+$pcname)
LogWrite `r`n
}
else{
LogWrite ("Could not find any CINIGA process")
LogWrite `r`n
}
}
}
This is the output I get:
PS User> powershell.exe -ExecutionPolicy Bypass –Noprofile -file "Path\to\script.ps1"
Looking for process nscp.exe in PC1...
Looking for process nscp.exe in PC2...
Looking for process nscp.exe in PC3...
Looking for process nscp.exe in PC4...
it doesn't seems to enter the 2nd foreach loop..
I was indeed pointing to localhost and changed to point to $pcName.
When i "turned" the if command with -ne instead of -eq it worked well!
Thank you for all your help !
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $($(Get-Date -Format yyyyMMdd-HHmmss) + " - " + $logstring)
}
$LogFile = "Path\To\LogFIle.txt" # Location of local log file name
$CsvLocation = "\\Path\To\Referance\CSV\Table.csv"
$csvdata = Import-Csv $CsvLocation
foreach($pc in $csvdata) {
$IcingaProc = "nscp"
$pcname = $pc.Name
Write-Host $("Looking for process "+ $IcingaProc + " in " + $pcname + "...")
if (Test-Connection -ComputerName $pcname -Count 1 -Quiet) {
$Processes = get-process -ComputerName $pcname | Where-Object {$_.ProcessName -Like "ns*"}
foreach($Proc in $Processes){
if ($Proc.ProcessName -ne $IcingaProc){
Write-Host ("Could not find any process")
}
else{
Write-Host "Program is installed succefully"
LogWrite ("Program is installed on "+$pcname)
}
}
}
else{
Write-Host ("The PC is not reachable!")
}
}
So I am trying to adapt this code I got from another post for the needs of my current organization. Please see below code:
$Admin = "Administrator","Public","Default","Administrator"
foreach($file in Get-ChildItem C:\Users\)
{
if ($file -in $Admin)
{
Write-Host = "`r`nUser account is" $file ". This is an Administrator Account, it will not be deleted."
}
else
{
Write-Host = "`r`nUser account is" $file ". Checking profiles age..."
$FileDate = (Get-item C:\Users\$file).CreationTime
Write-Host = $FileDate
$TestDate = (Get-Date).addDays(-30)
Write-Host = $TestDate
If ($FileDate -lt $TestDate)
{
Write-Host = "Since" $file "is older than 30 Days (" $FileDate ") it will be deleted."
$UserAccountPath = "C:\\Users\\$file"
$WMIQuery = "SELECT * FROM Win32_UserProfile WHERE localpath = '$UserAccountPath'"
$UserProfile = get-wmiobject win32_userprofile | where localpath -eq c:\\users\\$file
Remove-WmiObject -InputObject "$UserProfile"
}
else
{
Write-Host = "Since File is dated less than 30 days old (" $FileDate ") it will not need to be deleted."
}
}
}
After running this I get the following error:
Remove-WmiObject : Object reference not set to an instance of an object.
At line:28 char:17
+ Remove-WmiObject -InputObject "$UserProfile"
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Remove-WmiObject], NullReferenceException
+ FullyQualifiedErrorId : System.NullReferenceException,Microsoft.PowerShell.Commands.RemoveWmiObject
Any help is appreciated.
Basically, the mistake is in this line:
$UserProfile = Get-WmiObject win32_userprofile | Where localpath -EQ c:\\users\\$file
$UserProfile is null, and will always be null, because the equality comparer -eq is looking for an exact match, to put it into perspective:
'C:\some\path' -eq 'C:\\some\\path' # => False
[pscustomobject]#{
localPath = 'C:\some\path'
} | Where-Object localPath -EQ 'C:\\some\\path' # => Null
To fix this, you can do the following:
# $UserAccountPath = "C:\\Users\\$file" // This line is not needed
#
# DirectoryInfo objects have a FullName property for their Absolute Path
$UserProfile = Get-WmiObject win32_userprofile | Where-Object localpath -EQ $file.FullName
Remove-WmiObject -InputObject $UserProfile
$UserProfile = Get-WmiObject win32_userprofile | Where-Object localpath -EQ $file.FullName
$UserProfile.Delete()
Looking for advice on error handling in Powershell. I think I understand the concept behind using Try/Catch but I'm struggling on where to utilize this in my scripts or how granular I need to be.
For example, should I use the try/catch inside my functions and if so, should I insert the actions of my function inside the try or do I need to break it
down further? OR, should I try to handle the error when I call my function? Doing something like this:
Try{
Get-MyFunction
} catch{ Do Something"
}
Here's an example of a script I wrote which is checking for some indicators of compromise on a device. I have an application that will launch this script and capture the final output. The application requires the final output to be in the following format so any failure should generate this.
[output]
result=<0 or 1>
msg= <string>
Which I'm doing like this:
Write-Host "[output]"
Write-Host "result=0"
Write-Host "msg = $VariableContainingOutput -NoNewline
Two of my functions create custom objects and then combine these for the final output so I'd like to capture any errors in this same format. If one function generates an error, it should record these and continue.
If I just run the code by itself (not using function) this works but with the function my errors are not captured.
This needs to work on PowerShell 2 and up. The Add-RegMember and Get-RegValue functions called by this script are not shown.
function Get-ChangedRunKey {
[CmdletBinding()]
param()
process
{
$days = '-365'
$Run = #()
$AutoRunOutput = #()
$RunKeyValues = #("HKLM:\Software\Microsoft\Windows\CurrentVersion\Run",
"HKLM:\Software\Wow6432node\Microsoft\Windows\CurrentVersion\Run",
"HKU:\S-1-5-21-*\Software\Microsoft\Windows\CurrentVersion\Run",
"HKU:\S-1-5-21-*\Software\Wow6432node\Microsoft\Windows\CurrentVersion\Run"
)
Try{
$Run += $RunKeyValues |
ForEach-Object {
Get-Item $_ -ErrorAction SilentlyContinue |
Add-RegKeyMember -ErrorAction SilentlyContinue |
Where-Object {
$_.lastwritetime -gt (Get-Date).AddDays($days)
} |
Select-Object Name,LastWriteTime,property
}
if ($Run -ne $Null)
{
$AutoRunPath = ( $Run |
ForEach-Object {
$_.name
}
) -replace "HKEY_LOCAL_MACHINE", "HKLM:" -replace "HKEY_Users", "HKU:"
$AutoRunValue = $AutoRunPath |
Where-Object {
$_ -and $_.Trim()
} |
ForEach-Object {
Get-RegValue -path $_ -Name '*' -ErrorAction SilentlyContinue
}
}
#Build Custom Object if modified Run keys are found
if($AutorunValue -ne $null)
{
foreach ($Value in $AutoRunValue) {
$AutoRunOutput += New-Object PSObject -Property #{
Description = "Autorun"
path = $Value.path
value = $Value.value
}
}
}
Write-Output $AutoRunOutput
}catch{
$AutoRunOutput += New-Object PSObject -Property #{
Description = "Autorun"
path = "N/A"
value = "Error accessing Autorun data. $($Error[0])"
}
}
}
}
function Get-ShellIOC {
[CmdletBinding()]
param()
process
{
$ShellIOCOutput = #()
$ShellIOCPath = 'HKU:\' + '*' + '_Classes\*\shell\open\command'
Try{
$ShellIOCValue = (Get-Item $ShellIOCPath -ErrorAction SilentlyContinue |
Select-Object name,property |
ForEach-Object {
$_.name
}
) -replace "HKEY_LOCAL_MACHINE", "HKLM:" -replace "HKEY_Users", "HKU:"
$ShellIOCDetected = $ShellIOCValue |
ForEach-Object {
Get-RegValue -path $_ -Name '*' -ErrorAction SilentlyContinue
} |
Where-Object {
$_.value -like "*cmd.exe*" -or
$_.value -like "*mshta.exe*"
}
if($ShellIOCDetected -ne $null)
{
foreach ($ShellIOC in $ShellIOCDetected) {
$ShellIOCOutput += New-Object PSObject -Property #{
Description = "Shell_IOC_Detected"
path = $ShellIOC.path
value = $ShellIOC.value
}
}
}
Write-Output $ShellIOCOutput
}catch{
$ShellIOCOutput += New-Object PSObject -Property #{
Description = "Shell_IOC_Detected"
path = "N/A"
value = "Error accessing ShellIOC data. $($Error[0])"
}
}
}
}
function Set-OutputFormat {
[CmdletBinding()]
param()
process
{
$FormattedOutput = $AutoRunOutput + $ShellIOCOutput |
ForEach-Object {
"Description:" + $_.description + ',' + "Path:" + $_.path + ',' + "Value:" + $_.value + "|"
}
Write-Output $FormattedOutput
}
}
if (!(Test-Path "HKU:\")){
try{
New-PSDrive -PSProvider Registry -Root HKEY_USERS -Name HKU -ErrorAction Stop | Out-Null
}catch{
Write-Output "[output]"
Write-Output "result=0"
Write-Host "msg = Unable to Connect HKU drive" -NoNewline
}
}
$AutoRunOutput = Get-ChangedRunKey
$ShellIOCOutput = Get-ShellIOC
$FormattedOutput = Set-OutputFormat
Write-Output "[output]"
if ($FormattedOutput -eq $Null)
{
Write-Output "result=0"
Write-Host "msg= No Items Detected" -NoNewline
}
else
{
Write-Output "result=1"
Write-Host "msg=Items Detected: $($FormattedOutput)" -NoNewline
}
You have to know that there are 2 error types in PowerShell:
Terminating Errors: Those get caught automatically in the catch block
Non-Terminating Error: If you want to catch them then the command in question needs to be execution using -ErrorAction Stop. If it is not a PowerShell command but an executable, then you need to check stuff like the exit code or $?. Therefore I suggest wrapping your entire action in an advanced function on which you then call using -ErrorAction Stop.
Apart from that I would like to remark that PowerShell version 2 has already been deprecated. The reason for why non-terminating errors exists is because there are cases like for example processing multiple objects from the pipeline where you might not want it to stop just because it did not work for one object. And please do not use Write-Host, use Write-Verbose or Write-Output depending on the use case.
I've got a couple things that i'm working on. One of them is sort of an import/export thing i found on here. but i'm getting the following error
PS C:\Users\joeblogs> C:\Users\joeblogs\Scripts\Copy user data.ps1 Invalid assignment expression. The left hand side of an assignment
operator needs to be something that can be assigned to like a variable
or a property. At C:\Users\delpillay\Documents\Scripts\Copy user
data.ps1:16 char:12
+ $username = <<<< gc env:userame
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : InvalidLeftHandSide
I don't know where to start and i'm not sure what to try...
Below is the code:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop",
#"Downloads",
"Favorites",
"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = gc env:userame
$userprofile = gc env:userprofile
##$appData = gc env:localAPPDATA
###### Restore data section ######
if ([IO.Directory]::Exists($destination + "\" + $username + "\"))
{
$caption = "Choose Action";
$message = "A backup folder for $username already exists, would you like to restore the data to the local machine?";
$Yes = new-Object System.Management.Automation.Host.ChoiceDescription "&Yes","Yes";
$No = new-Object System.Management.Automation.Host.ChoiceDescription "&No","No";
$choices = [System.Management.Automation.Host.ChoiceDescription[]]($Yes,$No);
$answer = $host.ui.PromptForChoice($caption,$message,$choices,0)
if ($answer -eq 0)
{
write-host -ForegroundColor green "Restoring data to local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
write-host -ForegroundColor cyan " $f..."
Copy-Item -ErrorAction silentlyContinue -recurse $currentRemoteFolder $userprofile
if ($f -eq "AppData\Local\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Roaming\Mozilla") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
if ($f -eq "AppData\Local\Google") { rename-item $currentLocalFolder "$currentLocalFolder.old" }
}
rename-item "$destination\$username" "$destination\$username.restored"
write-host -ForegroundColor green "Restore Complete!"
}
else
{
write-host -ForegroundColor yellow "Aborting process"
exit
}
}
###### Backup Data section ########
#else
{
Write-Host -ForegroundColor green "Outlook is about to close, save any unsaved emails then press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Get-Process | Where { $_.Name -Eq "OUTLOOK" } | Kill
write-host -ForegroundColor green "Backing up data from local machine for $username"
foreach ($f in $folder)
{
$currentLocalFolder = $userprofile + "\" + $f
$currentRemoteFolder = $destination + "\" + $username + "\" + $f
$currentFolderSize = (Get-ChildItem -ErrorAction silentlyContinue $currentLocalFolder -Recurse -Force | Measure-Object -ErrorAction silentlyContinue -Property Length -Sum ).Sum / 1MB
$currentFolderSizeRounded = [System.Math]::Round($currentFolderSize)
write-host -ForegroundColor cyan " $f... ($currentFolderSizeRounded MB)"
Copy-Item -ErrorAction silentlyContinue -recurse $currentLocalFolder $currentRemoteFolder
}
$oldStylePST = [IO.Directory]::GetFiles($appData + "\Microsoft\Outlook", "*.pst")
foreach($pst in $oldStylePST)
{
if ((test-path -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")) -eq 0){new-item -type directory -path ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle") | out-null}
write-host -ForegroundColor yellow " $pst..."
Copy-Item $pst ($destination + "\" + $username + "\Documents\Outlook Files\oldstyle")
}
write-host -ForegroundColor green "Backup complete!"
}
Few observations:
You are not commenting the Favourites and My Documents. If you want to use them then use comma separated directly.
Use this:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
You have missed the n in username:
$username = gc env:username
Donot use gc env:username. Instead directly access them like this below: Completely reframed:
$destination = "E:\Users\%username%\Backup"
$folder = "Desktop","Favorites","My Documents"
#"Downloads",
#"Favorites",
#"My Documents",
#"Music",
#"Pictures",
#"Videos",
#"AppData\Local\Mozilla",
#"AppData\Local\Google",
#"AppData\Roaming\Mozilla"
######################################
$username = $env:username
$userprofile = $env:userprofile
##$appData = gc env:localAPPDATA
These are the major things that have been fixed. Hope it helps you.
I have the following function that works correctly in 2012r2, when i run it in 2008R2 it throws the below error. The surprising thing is that if i execute it a second time, it works without any issue!!
# Function Reg-Stamp {
Param(
[Parameter(Mandatory=$True)]
[string]$Phase
)
$msg = "`nEntering: $((Get-Variable MyInvocation -Scope 0).Value.MyCommand.Name)" ; Write-Host -fore Gray $msg ; $msg | out-file -Append $log
$RegStampInfo = Build-Variable $RegStampCSV
$Version = ($ScriptVersionInfo | Where-Object {$_.Parameter -eq "Version" -and $_.Phase -eq $Phase }).Value
$DisplayName = ($ScriptVersionInfo | Where-Object {$_.Parameter -eq "DisplayName" -and $_.Phase -eq $Phase }).Value
$BuildDate = ($ScriptVersionInfo | Where-Object {$_.Parameter -eq "BuildDate" -and $_.Phase -eq $Phase }).Value
$RunDate = Get-Date
$Success = $(-not($CriticalError))
$msg = "`nUpdating registry with build information"; Write-Host -fore Gray $msg; $msg | out-file $log -Append;
$RegStampInfo | Where-Object {($_.Phase.ToLower()) -eq ($Phase.ToLower())} | foreach-Object {
$ValueData = $(get-variable -Name $($_.StampData) -ValueOnly -ErrorAction SilentlyContinue)
$msg = "Adding Key: $($_.StampKey) '$($_.StampValue)' '$ValueData'"; Write-Host -fore Green "$msg"; $msg | out-file $log -Append;
New-Item -Path $($_.StampKey) -ErrorAction SilentlyContinue
Set-ItemProperty -Path $_.StampKey -name $_.StampValue -Value $ValueData
}
$msg = "`nExiting: $((Get-Variable MyInvocation -Scope 0).Value.MyCommand.Name)"; Write-Host -fore DarkGreen $msg ; $msg | out-file -Append $log
#}
I get the following error:
out-lineoutput : The object of type "Microsoft.PowerShell.Commands.Internal.For
mat.FormatStartData" is not valid or not in the correct sequence. This is likel
y caused by a user-specified "format-table" command which is conflicting with t
he default formatting.
+ CategoryInfo : InvalidData: (:) [out-lineoutput], InvalidOperat
ionException
+ FullyQualifiedErrorId : ConsoleLineOutputOutOfSequencePacket,Microsoft.P
owerShell.Commands.OutLineOutputCommand
I have seen similar error in powershell when using format-table however i am not using fr here atleast directly.
Not sure what is wrong!
EDIT:
no, but it turns out that the issue was not in the above script at all.
It appears that caller script had a line involving format-table which had nothing to do with this script, was causing the issue.
$SupportFilesInfo = Import-csv $SupportFilesCSV | select-object
$SupportFilesInfo | ft ; $SupportFilesInfo | Out-File -append $log
I changed it to:
$SupportFilesInfo | ft | out-default; $SupportFilesInfo | Out-File -append $log
which resolved the error!!
However i am still at loss at why the error occurs ONLY during the first run.
I had hit this issue earlier too, but it was very consistent.
Any idea why?