How do I edit a #{ that powershell automatically puts registry keys ins? - powershell

so I have a powershell command that grabs a registry key. But when using select-pattern, it always sticks the whole key within a line, and I've been googling everywhere but no where is telling me how to only grab certain language from the line, and not return the full like. Like below:
Line : #{t0_recursive=\\ad\; t7_recursive=\\ad\chrolit; t5_recursive=\\ad\arolit; t3_recursive=k:\;
t9_recursive=\\ad\fwrolit; t10_recursive=\\ad\larolit; t15_recursive=\\ad\slrolit;
t6_recursive=\\ad\brolit; t11_recursive=\\ad\mirolit; t14_recursive=\\ad\sfrolit;
t12_recursive=\\ad\nyrolit; t2_recursive=j:\; t16_recursive=\\ad\enfcases; t1_recursive=f:\;
t8_recursive=\\ad\drolit; t4_recursive=m:\; t13_recursive=\\ad\plrolit; t17_recursive=C:\ENFProcessing;
t129=c:\enfprocessing; t99=\\ad\slrolit; t9=\\ad\; t84=\\ad\sfrolit; t69=\\ad\plrolit; t54=\\ad\nyrolit;
t39=\\ad\mirolit; t279_recursive=C:\Users; t264=\\ad\fwrolit; t249=\\ad\drolit; t24=\\ad\larolit;
t234=\\ad\chrolit; t219=\\ad\brolit; t204=\\ad\arolit; t159_recursive=\\AD.SEC.GOV\Projects;
t144_recursive=\\AD.SEC.GOV\users; t114=\\ad\enfcases;
PSPath=Microsoft.PowerShell.Core\Registry::HKLM\SOFTWARE\Policies\Adobe\Adobe
Acrobat\DC\FeatureLockDown\cTrustedFolders\cAlwaysTrustedForJavaScript;
PSParentPath=Microsoft.PowerShell.Core\Registry::HKLM\SOFTWARE\Policies\Adobe\Adobe
Acrobat\DC\FeatureLockDown\cTrustedFolders; PSChildName=cAlwaysTrustedForJavaScript;
PSProvider=Microsoft.PowerShell.Core\Registry}
I get this output, but can't just return PSPath and the C:\Users* from the line, it always returns the full line.
Why is this?
$value = Get-ChildItem -Path 'Registry::HKEY_CURRENT_USER\SOFTWARE\Adobe\Adobe Acrobat\DC\TrustManger\cTrustedFolder' 2>NULL | findstr : | measure-object -line | select-object -expandproperty lines ; if ( $value -lt 1 ) { echo 'PASSED, HKEY_CURRENT_USER\SOFTWARE\Adobe\Adobe Acrobat\DC\TrustManger\cTrustedFolder is NULL' } else { $value2 = Get-ChildItem -Path 'Registry::HKEY_CURRENT_USER\SOFTWARE\Adobe\Adobe Acrobat\DC\TrustManger\cTrustedFolder' -Recurse | ForEach-Object {Get-ItemProperty -Path $_.PSPath -Name t*} |Select-String -Pattern 'c:\\users*' | select -ExpandProperty Line| findstr /i /V /c:Desktop, /c:downloads|format-list LineNumber,Line
I tried googling how to edit lines in powershell, to no avail.

Take out findstr. It turns objects to strings.
The get-childitem output is a bit confusing for the registry. The formatting file $PSHOME\Registry.format.ps1xml runs get-itemproperty. Use get-itemproperty to get registry values.
I have a replacement for get-itemproperty that can be used for recursive searches here: Use PowerShell to search for string in registry keys and values
For example:
get-childitem -recurse hklm:\software\adobe | get-itemproperty2 |
? name -like distiller*
Path Name Value Type
---- ---- ----- ----
HKLM:\software\adobe\Acrobat Distiller\10.0\Uninstall Distiller 1 DWord

Related

Powershell, registry and wildcards, oh my

Given...
HKLM\Software\
KeyName
Property_1
Property_2
Property_[0-1]
Key*Name
Property_1
Property_2
Property_[0-1]
Key#Name
Property_1
Property_2
Property_[0-1]
I can use
Get-Item -path:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
which will return KeyName, Key*Name and Key#Name, while
Get-Item -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
will return just Key*Name. So far, so good. I can use -path or -literalPath as needed to either search for a key with wildcards or not. But properties pose a problem.
Get-ItemProperty -path:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\KeyName" -name:"Prop_[0-9]"
works as expected and returns Prop_1 & Prop_2 from the KeyName key. And
Get-ItemProperty -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\KeyName" -name:"Prop_[0-9]"
works as expected and returns just Prop_[0-9] from the same key. But it all fails apart when you need to use a wildcard to find properties, in a path that includes a wildcard character as a literal in the key path. So...
Get-ItemProperty -path:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name" -name:"Prop_[0-9]"
returns Prop_1 & Prop_2 from all three keys. Not the desired behavior at all.
I had hoped to be able to filter on PSPath using -`literalPath' but this
Get-ItemProperty -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name" -name:"Prop_[0-9]" | where {$_.PSPath -match [RegEx]::Escape("Key*Name")}
does not return the correct properties. It seems that a -literalPath means a literal name also. So I tried filtering on PSPath and Name like so
Get-ItemProperty -literalPath:"Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name" -name:"Prop_[0-9]" | where {(($_.PSPath -match [RegEx]::Escape("Key*Name")) -and ($_.Name -match "Prop_[0-9]"))}
But that doesn't work because once you actually get real properties, they are no longer a .NET type, they have been shat into a PSCustomObject.
And that is starting to get so complicated I wonder if there is a better way to proceed. I should note that the ultimate goal here is to get both a literal path and a list of literal property names, so that I can move, copy or delete the properties. So, given a path of Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name and a name of Prop_[0-9] I will eventually want to, for example, delete
HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name\Prop_1
&
HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name\Prop_2
but not
HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name\Prop_[0-9]
EDIT: Based on the answer from #Tomalak I have simplified a bit, to simply get back a list of property names. That looks like this
$keyPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
$propExpr = "Prop_[0-9]"
((Get-Item -literalPath:$keyPath | Get-ItemProperty).PSObject.Properties | Where-Object Name -Match $propExpr | ForEach-Object {$_.Name})
This will get a registry key by literal path and filter its properties by regex match
$keyPath = "Registry::HKEY_LOCAL_MACHINE\SOFTWARE\Key*Name"
$propExpr = "Prop_[0-9]"
Get-Item -literalPath $keyPath -PipelineVariable key | Get-ItemProperty | ForEach-Object {
$_.PSObject.Properties | Where-Object Name -Match $propExpr | ForEach-Object {
[pscustomobject]#{
key = $key.Name
prop = $_.Name
value = $_.Value
}
}
}
Instead of the $key.Name you can of course return the actual $key if that's more convenient for your task.

Filter PowerShell command Output

I want to change a mapped drive remote path. but I'm unable to filter the remote path property. Is there any way I can filter all the mapped drives remote path only so, later on, I can run a foreach loop to change the values? Thanks.
Get-Item -Path HKCU:\Network | Where-Object -FilterScript {'RemotePath'}
Hive: HKEY_CURRENT_USER\Network
Name Property
---- --------
Z RemotePath : \\IAPC\Users\IA\Documents\10
UserName :
ProviderName : Microsoft Windows Network
ProviderType : 131072
ConnectionType : 1
ConnectFlags : 0
DeferFlags : 4
UseOptions : {68, 101, 102, 67...}
PS HKCU:\Network>
Only interested in name of the network drive with RemotePath (under Property column)
The following outputs [pscustomobject] instances representing the values of those registry subkeys of HKCU:\Network whose RemotePath value is non-empty:
Get-ItemProperty -Path HKCU:\Network\* | Where-Object RemotePath
To get just the drive-name-remote-path pairs:
Get-ItemProperty -Path HKCU:\Network\* | Where-Object RemotePath |
Select-Object PSChildName, RemotePath
Note: The PSChildName property contains the drive letter of each mapping (it is the name of the subkey whose values are being returned).
To loop over all mappings of interest and update them by replacing the server-name component:
$oldServer = '\\IAPC\'
$newServer = '\\localhost\'
# CAVEAT: This instantly updates your drive mappings.
# You can add -WhatIf to the Set-ItemProperty call,
# to *preview* the operation, but it will only show the
# target registry key and value, not the new data.
Get-ItemProperty -Path HKCU:\Network\* | Where-Object RemotePath | ForEach-Object {
$driveLetter, $remotePath = $_.PSChildName, $_.RemotePath
Set-ItemProperty -LiteralPath HKCU:\Network\$driveLetter RemotePath ($remotePath -replace [regex]::Escape($oldServer), $newServer)
}
As for what you tried:
Where-Object -FilterScript {'RemotePath'} is a no-op, because any input meets the criterion 'RemotePath', which, as a non-empty string literal is invariably $true when interpreted as a Boolean. To access a property on the current input object, you need to use automatic $_ variable: { $_.RemotePath }
It is only with the simplified syntax, shown above, which doesn't use a script block ({ ... }) that the string argument given is implicitly interpreted as the name of the property to access on the input object at hand.
Get-Item -Path HKCU:\Network only targets the root key of all network mappings itself, not the subkeys that define the actual mappings.
In your case you're also interested in the RemotePath value of each subkey, which Get-ItemProperty provides for all subkeys, targeted with a wildcard pattern, HKCU:\Network\*
Get-ItemProperty, when not given a property name, returns all properties - which in the case at hand are registry values - as a "property bag", in the form of a [pscustomobject] instance, with the name of the containing key reported in the .PSChildName property.
Unfortunately, working with PowerShell's registry provider is often not as straightforward as one would like.
#imtiaz Hey i am not sure if you are trying the same but here is how i have achieved recently.
$oldServer = "\\abc.local"
$newServer = "\abc.com"
$paths = REG QUERY HKCU\Network | where{$_ -ne ""}
foreach ($item in $paths)
{
$oldPath = REG QUERY $item /f RemotePath /t REG_SZ | Out-String
$oldPath1 = $oldPath.Split()[-12]
$updatedPath = $oldPath1 -replace $oldServer,$newServer
reg add $item /v RemotePath /t REG_SZ /d $updatedPath /f /reg:64
}
I tried before Get-ItemProperty and set-itemproperty was facing some issues. This might help you. I know this's not a "professional way" but this worked for me.

create file index manually using powershell, tab delimited

Sorry in advance for the probably trivial question, I'm a powershell noob, please bear with me and give me advice on how to get better.
I want to achieve a file index index.txt that contains the list of all files in current dir and subdirs in this format:
./dir1/file1.txt 07.05.2020 16:16 1959281
where
dirs listed are relative (i.e. this will be run remotely and to save space, the relative path is good enough)
the delimiter is a tab \t
the date format is day.month.fullyear hours:minutes:seconds, last written (this is the case for me, but I'm guessing this would be different on system setting and should be enforced)
(the last number is the size in bytes)
I almost get there using this command in powershell (maybe that's useful to someone else as well):
get-childitem . -recurse | select fullname,LastWriteTime,Length | Out-File index.txt
with this result
FullName LastWriteTime Length
-------- ------------- ------
C:\Users\user1\Downloads\test\asdf.txt 07.05.2020 16:19:29 1490
C:\Users\user1\Downloads\test\dirtree.txt 07.05.2020 16:08:44 0
C:\Users\user1\Downloads\test\index.txt 07.05.2020 16:29:01 0
C:\Users\user1\Downloads\test\test.txt 07.05.2020 16:01:23 814
C:\Users\user1\Downloads\test\text2.txt 07.05.2020 15:55:45 1346
So the questions that remain are: How to...
get rid of the headers?
enforce this date format?
tab delimit everything?
get control of what newline character is used (\n or \r or both)?
Another approach could be this:
$StartDirectory = Get-Location
Get-ChildItem -Path $StartDirectory -recurse |
Select-Object -Property #{Name='RelPath';Expression={$_.FullName.toString() -replace [REGEX]::Escape($StartDirectory.ToString()),'.'}},
#{Name='LastWriteTime';Expression={$_.LastWriteTime.toString('dd.MM.yyyy HH:mm:ss')}},
Length |
Export-Csv -Path Result.csv -NoTypeInformation -Delimiter "`t"
I recommend to use proper CSV files if you have structured data like this. The resulting CSV file will be saved in the current working directory.
If the path you are running this from is NOT the current scrip path, do:
$path = 'D:\Downloads' # 'X:\SomeFolder\SomeWhere'
Set-Location $path
first.
Next, this ought to do it:
Get-ChildItem . -Recurse -File | ForEach-Object {
"{0}`t{1:dd.MM.yyyy HH:mm}`t{2}" -f ($_ | Resolve-Path -Relative), $_.LastWriteTime, $_.Length
} | Out-File 'index.txt'
On Windows the newline will be \r\n (CRLF)
If you want control over that, this should do:
$newline = "`n" # for example
# capture the lines as string array in variable $lines
$lines = Get-ChildItem . -Recurse -File | ForEach-Object {
"{0}`t{1:dd.MM.yyyy HH:mm}`t{2}" -f ($_ | Resolve-Path -Relative), $_.LastWriteTime, $_.Length
}
# join the array with the chosen newline and save to file
$lines -join $newline | Out-File 'index.txt' -NoNewline
Because your requirement is to NOT have column headers in the output file, I'm using Out-File here instead of Export-Csv

How to view a list of descriptions of available COM objects in PowerShell?

When I execute this powershell command to get a list of running COM objects that match the prefix "Python", I get the following output:
PS C:\Users\{path-to-arbitrary-directory}> Get-ChildItem HKLM:\Software\Classes | Where-Object {
$_.PSChildName -match '^Python[\.a-zA-Z]*$' } | Select-Object
Hive: HKEY_LOCAL_MACHINE\Software\Classes
Name Property
---- --------
Python (default) : Python ActiveX Scripting Engine
Python.Dictionary (default) : Python Dictionary
Python.Interpreter (default) : Python Interpreter
Python.TestServer (default) : Python Test COM Server
What I would like to do is just get a list of the Name and Description.
Currently I am able to get the names with this command:
PS C:\Users\{path-to-arbitrary-directory}> Get-ChildItem HKLM:\Software\Classes | Where-Object {
$_.PSChildName -match '^Python[\.a-zA-Z]*$' } | Select-Object PSChildName,Property
PSChildName Property
----------- --------
Python {(default)}
Python.Dictionary {(default)}
Python.Interpreter {(default)}
Python.TestServer {(default)}
But I can't for the life of me figure out how to show the Descriptions that I see when I execute the 1st command?
This is the output I would want:
Name Description
---- --------
Python Python ActiveX Scripting Engine
Python.Dictionary Python Dictionary
Python.Interpreter Python Interpreter
Python.TestServer Python Test COM Server
(if it helps anyone, I am also able to view the description with this command)
PS C:\Users\{path-to-arbitrary-directory}> Get-ChildItem HKLM:\Software\Classes | Where-Object {
$_.PSChildName -match '^Python[\.a-zA-Z]*$' } | Get-ItemProperty
(default) : Python ActiveX Scripting Engine
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Classes\Python
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\Software\Classes
PSChildName : Python
PSProvider : Microsoft.PowerShell.Core\Registry
...
One way to do this is the use of calculated properties. You can use Get-ItemProperty to get the value of the (default) registry key property. Then you can show this value as a calculated property.
Get-ChildItem HKLM:\software\Classes\ |
Where-Object {$_.PSChildName -match 'document'} |
Select-Object PSChildName, #{Name = "Default"; Expression = {($_ | Get-ItemProperty)."(default)"}}
Try this out. Things like this are better in functions.
function Get-COMDescription {
Param(
[parameter(Mandatory=$true)][string]$Search
)
Get-ChildItem HKLM:\Software\Classes | Where-Object {
# Match naming convention for COM Object ensure they key has a CLSID folder.
$_.PSChildName -match "^$Search\.\w+$" -and (Test-Path -Path "$($_.PSPath)\CLSID") } |
Select-Object PSChildName,#{l="Description";e={$_ | Get-ItemProperty | select -ExpandProperty "(default)" }}
}
Usage example:
PS C:\> Get-COMDescription -Search GoogleUpdate
PSChildName Description
----------- -----------
GoogleUpdate.CoCreateAsync CoCreateAsync
GoogleUpdate.CoreClass Google Update Core Class
GoogleUpdate.CoreMachineClass Google Update Core Class
GoogleUpdate.CredentialDialogMachine GoogleUpdate CredentialDialog
GoogleUpdate.OnDemandCOMClassMachine Google Update Broker Class Factory
GoogleUpdate.OnDemandCOMClassMachineFallback Google Update Legacy On Demand
GoogleUpdate.OnDemandCOMClassSvc Google Update Legacy On Demand
GoogleUpdate.PolicyStatus Google Update Policy Status Class
GoogleUpdate.ProcessLauncher Google Update Process Launcher Class
GoogleUpdate.Update3COMClassService Update3COMClass
GoogleUpdate.Update3WebMachine Google Update Broker Class Factory
GoogleUpdate.Update3WebMachineFallback GoogleUpdate Update3Web
GoogleUpdate.Update3WebSvc GoogleUpdate Update3Web
The existing answers are helpful, but let me add some background information:
The reason that the default output shows the target keys' values is that the default output formatting enumerates them, as this command reveals:
(Get-FormatData Microsoft.Win32.RegistryKey -PowerShellVersion $PSVersionTable.PSVersion).FormatViewDefinition.Control.Rows.Columns.DisplayEntry.Value
This shows:
PSChildName # column 1 - below is the script block that defines column 2
$result = (Get-ItemProperty -LiteralPath $_.PSPath |
Select * -Exclude PSPath,PSParentPath,PSChildName,PSDrive,PsProvider |
Format-List | Out-String | Sort).Trim()
$result = $result.Substring(0, [Math]::Min($result.Length, 5000) )
if($result.Length -eq 5000) { $result += "..." }
$result
As you can see, Get-ItemProperty is called behind the scenes to enumerate a key's values.
As an aside: This method of enumerating values as part of the formatting leads to incorrect output when retrieving values from a remote registry - see this answer.
While calling Get-ItemProperty in the script block of a calculated property, as shown in the other answers, definitely works, there is a more efficient alternative: You can call the .GetValue() method of the Microsoft.Win32.RegistryKey instances that Get-Item outputs:
Get-ChildItem HKLM:\Software\Classes |
Where-Object PSChildName -match '^Python[\.a-z]*$' |
Select-Object #{ n='Name'; e='PSChildName' },
#{ n='(default)'; e={ $_.GetValue('') } }

Getting a specific process id from a powershell output

Hi I'm new to powershell scripting and I would like to retrieve a specific process id based on the file's hash. However I can only get either a table with the hash value or a table with the id,process name and path
$ps = Get-Process | Select-Object -Property Id,ProcessName,Path
$hashlist = Get-FileHash(Get-Process|Select-Object -ExpandProperty Path) -Algorithm MD5
Is it possible for me to merge the two tables together so that I can get a view of Id,ProcessName and Hash using the path to link them together?
EDIT: totally different approach due to new information from comment
I don't think it is so easy to identify malware with a file MD5 hash.
Modern AntiVirusSoftware uses heuristics to overcome the problem of mean malware which includes random data and also obfuscates it's origin.
## Q:\Test\2018\11\11\SO_53247430.ps1
# random hex string replace with your malware signature
$MalwareMD5Hash = 'D52C11B7E076FCE593288439ABA0F6D4'
Get-Process | Where-Object Path | Select-Object ID,Path | Group-Object Path | ForEach-Object {
if ($MalwareMD5Hash -eq (Get-FileHash $_.Name -Alg MD5).Hash){
##iterate group to kill all processes matching
ForEach ($PID in $_.Group.ID){
Stop-Process -ID $PID -Force -WhatIF
}
}
$_.Name | Remove-Item -Force -WhatIf # to delete the physical file.
}
As I suggested in my comment:
$HashList = [ordered]#{}
Get-Process |Where-Object Path | Select-Object Path |Sort-Object Path -Unique | ForEach-Object {
$HashList[$_.Path]=(Get-FileHash $_.Path -Alg MD5).Hash
## or the reverse, the hash as key and the path as value
# $HashList[(Get-FileHash $_.Path -Alg MD5).Hash]=$_.Path
}
$Hashlist | Format-List
Shorted sample output
Name : C:\Program Files\Mozilla Firefox\firefox.exe
Value : BFE829AB5A4B729EE4565700FC8853DA
Name : C:\WINDOWS\Explorer.EXE
Value : E4A81EDDFF8B844D85C8B45354E4144E
Name : C:\WINDOWS\system32\conhost.exe
Value : EA777DEEA782E8B4D7C7C33BBF8A4496
Name : C:\WINDOWS\system32\DllHost.exe
Value : 2528137C6745C4EADD87817A1909677E
> $hashlist['C:\WINDOWS\Explorer.EXE']
E4A81EDDFF8B844D85C8B45354E4144E
Or with the reversed list
> $hashlist['E4A81EDDFF8B844D85C8B45354E4144E']
C:\WINDOWS\Explorer.EXE