How To Encode A String To REG_BINARY With Powershell - powershell

I am trying to write a value to a REG_BINARY using Powershell. I can write to the REG_BINARY if I provide the data, but I need to be able to encode what I want to write in the file so I can use system and date variables. Below is my code and error.
If you uncomment the first $data it will work.
function Convert-ToCHexString
{
param ([String] $str)
$ans = ''
[System.Text.Encoding]::ASCII.GetBytes($str) | % { $ans += "0x{0:x2}," -f $_ }
return $ans.Trim(' ',',')
}
$Folder = Convert-ToCHexString Z:\
$Username = Convert-ToCHexString $env:USERNAME
$Filename = Convert-ToCHexString \archive.pst
$key = "HKCU:\Software\Microsoft\Windows NT\CurrentVersion\Windows Messaging Subsystem\Profiles\Outlook\0a0d020000000000c000000000000046"
#$data = 0x5a,0x3a,0x5c,0x61,0x72,0x63,0x68,0x69,0x76,0x65,0x2e,0x70,0x73,0x74
$data = $Folder + "," + $Username + "," + $Filename + ",0x00,0x00,0x00"
Set-ItemProperty -path $key -name "001f0324" -value ([byte[]]($data))
Below is the error I get:
Cannot convert value "0x5a,0x3a,0x5c,0x6a,0x62,0x79,0x65,0x72,0x73,0x5c,0x61,0x72,0x63,0x68,0x69,0x76,0x65,0x2e,0x70,0x73,0x74,0x00,0x00,0x00" to type"System.Byte[]". Error: "Cannot convert value "0x5a,0x3a,0x5c,0x6a,0x62,0x79,0x65,0x72,0x73,0x5c,0x61,0x72,0x63,0x68,0x69,0x76,0x65,0x2e,0x70,0x73,0x74,0x00,0x00,0x00" to type "System.Byte". Error: "Additional non-parsable characters are at the end of the string.""

If the property is expecting a byte array use [Text.Encoding]::Unicode.GetBytes($data). Not sure what's up with the 0x00,0x00,0x00 at the end but if you need that, just append three 0's to the end of the byte array before passing it to Set-ItemProperty.

Your function Convert-ToCHexString returns a single string with your items separated by commas.
The cast ([byte[]]($data)) that you perform will only work if your string contains only a single hex value or is an array of values it can parse this way. It won't split the string for you.
However, the GetBytes function already returns a byte array, so there's no need to convert it to a string and then back again. Also, based on your comments, it looks like outlook wants these values to be encoded as Unicode (UTF-16), which is not surprising since that's the windows default. You'll also need to null terminate your byte array (the final two 0's you see from outlook). That makes your function look like this:
function Convert-ToCHexString
{
param ([String] $str)
$ans = ''
[System.Text.Encoding]::Unicode.GetBytes($str + "`0")
}

Related

Powershell passing multiple parameters from one script to another [duplicate]

I've seen the # symbol used in PowerShell to initialise arrays.
What exactly does the # symbol denote and where can I read more about it?
In PowerShell V2, # is also the Splat operator.
PS> # First use it to create a hashtable of parameters:
PS> $params = #{path = "c:\temp"; Recurse= $true}
PS> # Then use it to SPLAT the parameters - which is to say to expand a hash table
PS> # into a set of command line parameters.
PS> dir #params
PS> # That was the equivalent of:
PS> dir -Path c:\temp -Recurse:$true
PowerShell will actually treat any comma-separated list as an array:
"server1","server2"
So the # is optional in those cases. However, for associative arrays, the # is required:
#{"Key"="Value";"Key2"="Value2"}
Officially, # is the "array operator." You can read more about it in the documentation that installed along with PowerShell, or in a book like "Windows PowerShell: TFM," which I co-authored.
While the above responses provide most of the answer it is useful--even this late to the question--to provide the full answer, to wit:
Array sub-expression (see about_arrays)
Forces the value to be an array, even if a singleton or a null, e.g. $a = #(ps | where name -like 'foo')
Hash initializer (see about_hash_tables)
Initializes a hash table with key-value pairs, e.g.
$HashArguments = #{ Path = "test.txt"; Destination = "test2.txt"; WhatIf = $true }
Splatting (see about_splatting)
Let's you invoke a cmdlet with parameters from an array or a hash-table rather than the more customary individually enumerated parameters, e.g. using the hash table just above, Copy-Item #HashArguments
Here strings (see about_quoting_rules)
Let's you create strings with easily embedded quotes, typically used for multi-line strings, e.g.:
$data = #"
line one
line two
something "quoted" here
"#
Because this type of question (what does 'x' notation mean in PowerShell?) is so common here on StackOverflow as well as in many reader comments, I put together a lexicon of PowerShell punctuation, just published on Simple-Talk.com. Read all about # as well as % and # and $_ and ? and more at The Complete Guide to PowerShell Punctuation. Attached to the article is this wallchart that gives you everything on a single sheet:
You can also wrap the output of a cmdlet (or pipeline) in #() to ensure that what you get back is an array rather than a single item.
For instance, dir usually returns a list, but depending on the options, it might return a single object. If you are planning on iterating through the results with a foreach-object, you need to make sure you get a list back. Here's a contrived example:
$results = #( dir c:\autoexec.bat)
One more thing... an empty array (like to initialize a variable) is denoted #().
The Splatting Operator
To create an array, we create a variable and assign the array. Arrays are noted by the "#" symbol. Let's take the discussion above and use an array to connect to multiple remote computers:
$strComputers = #("Server1", "Server2", "Server3")<enter>
They are used for arrays and hashes.
PowerShell Tutorial 7: Accumulate, Recall, and Modify Data
Array Literals In PowerShell
I hope this helps to understand it a bit better.
You can store "values" within a key and return that value to do something.
In this case I have just provided #{a="";b="";c="";} and if not in the options i.e "keys" (a, b or c) then don't return a value
$array = #{
a = "test1";
b = "test2";
c = "test3"
}
foreach($elem in $array.GetEnumerator()){
if ($elem.key -eq "a"){
$key = $elem.key
$value = $elem.value
}
elseif ($elem.key -eq "b"){
$key = $elem.key
$value = $elem.value
}
elseif ($elem.key -eq "c"){
$key = $elem.key
$value = $elem.value
}
else{
Write-Host "No other value"
}
Write-Host "Key: " $key "Value: " $value
}

How in general can I find functions that contains bugs due to array output?

Unassigned variables are outputted in PowerShell functions. So for instance in the function below you will get an object array returned instead of a string because [regex] is unassigned and needs to become assigned or nullified or whatever.
These bugs are very hard to detect when scanning codebases. Since some functions DO output an object array willingly (and then correctly handled) while for others it is indeed a bug. In this case the output was used to write somewhere and since an array gets transformed to a string it was undetectable.
function Get-PascalizedString {
param(
[string]$String
)
$rx = "(?:[^a-zA-Z0-9]*)(?<first>[a-zA-Z0-9])(?<reminder>[a-zA-Z0-9]*)(?:[^a-zA-Z0-9]*)"
$result = ""
[regex]::Matches($String, $rx) | ForEach-Object {$_.Groups} {
$TextInfo = (Get-Culture).TextInfo
$part = $TextInfo.ToTitleCase($_.Value.ToLower()).Trim()
$part = $part -replace "[^a-zA-Z0-9]"
$result = $result + $part
}
return $result
}
$a = Get-PascalizedString -String "aaa"
write-host $a
$a.GetType() // but...
So is there a smart way to detect these kind of bugs in larger codebases?

Powershell : Update properties file with key = value

My requirement is, I have a properties file say C:\google\configuration\backup\configuration.properties
with content shown below
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{49e5d325-8065-49f4-bf0d-r4be94cc1feb}\\
backup.max.count = 10
I have a method that takes key and value as input.
function Script:change_or_replace_value([string]$key, [string]$value) {
$origional_file_content = Get-Content $CONF_FILE_LOCATION
$key_value_map = ConvertFrom-StringData($origional_file_content -join [Environment]::NewLine)
$old_value = $key_value_map.$key
$Old_file_pattern = "$key = $old_value"
$new_file_pattern = "$key = $value"
$origional_file_content | ForEach-Object {$_ -Replace $Old_file_pattern, $new_file_pattern} | Set-Content $NEW_FILE_LOCATION
}
If key is "backup.volume.guid" and value is "\\?\Volume{111111-222-222-444-r4be94cc1feb}\"
method should replace the text
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{111111-222-222-444-r4be94cc1feb}\\
backup.max.count = 10
If key is "backup.volume.guid" and value is "" method should remove the line
backup.path = C:\\ProgramData\\google\\backup
backup.max.count = 10
If the value is empty delete the line else replace the text for the given key.
It contains special character like \ or other characters
How to delete the content if the key exists and value is an empty string
Your current approach has two problems, based on your attempt to update the properties by string manipulation via the file content as a single string:
In the ForEach-Object script block you'd need a different command to eliminate a line, because the -replace operator always returns something: if the regex pattern doesn't match the input, the input string is passed through.
You're missing an additional string-replacement step: ConvertFrom-StringData considers \ an escape character, so any pair of \\ in the input file turns into a single \ in the resulting hashtable. Therefore, you'll also have to double the \\ in $oldvalue and $value in order for the string replacement on the original file content to work.
Also, -replace, because it expects regex (regular expression) as the search operand, requires metacharachters such as \ to be escaped by \-escaping them; you could do that with [regex]::Escape($Old_file_pattern).
I suggest a different approach that avoids these problems, namely:
Directly modify the hashtable that ConvertFrom-StringData returns.
Then serialize the updated hashtable to the output file, using string formatting.
As part of the string formatting, ouble the \ in the values again by using the [string] type's .Replace() method, which operates on literal strings and is simpler (and faster) in this case; however, you could also use the somewhat counter-intuitive -replace '\\', '\\'
# Assign your real path here.
$OCUM_CONF_FILE_LOCATION = 'in.properties'
# Only for demonstration here: create a sample input file.
#'
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{49e5d325-8065-49f4-bf0d-r4be94cc1feb}\\
backup.max.count = 10
'# > $OCUM_CONF_FILE_LOCATION
# Function which either updates, adds, or removes an entry.
# NOTE:
# * This function updates input file $OCUM_CONF_FILE_LOCATION *in place*.
# To be safe, be sure to have a backup copy before you try this.
# * Set-Content's default character encoding is used to save the updated file.
# Use the -Encoding parameter as needed.
function Update-PropertiesFile ([string]$key, [string]$value) {
$ht = ConvertFrom-StringData (Get-Content -Raw $OCUM_CONF_FILE_LOCATION)
if ($ht.Contains($key)) { # update or delete existing entry
if ('' -eq $value) { $ht.Remove($key) }
else { $ht[$key] = $value }
} elseif ('' -eq $value) { # entry to remove not found
Write-Warning "No entry with key '$key' found; nothing to remove."
return
} else { # new entry
$ht[$key] = $value
}
# Serialize the updated hashtable back to the input file.
Set-Content $OCUM_CONF_FILE_LOCATION -Value $(
foreach ($key in $ht.Keys) {
'{0} = {1}' -f $key, $ht[$key].Replace('\', '\\')
}
)
}

PowerShell -Replace appending (oddly) instead of replacing

EDIT: PowerShell version is 5.1
I am writing some code that will take the value of a variable from a file, and if that string is made up of other variables within the file, it will locate them and replace it to reconstruct the run-time value. It assumes the variable contains a string, and that the string describes a directory.
For example, the file contains:
$var0 = "C:\Users\v-anad\Documents"
$var1 = "$var0\TestFolder"
Then when the code looks for $var1, it should return something like: "C:\Users\v-anad\Documents\TestFolder\"
However, the actual output I see is:
\TestFolder"-anad\Documents"
When it replaces, it deletes the correct substring ($var0), but when it inserts the value of $var0, it skips over the characters that existed there before, and appends the remaining characters to the end of the string. I have no clue what/where I've gone wrong.
Here is the code in question:
function Get-Var-Value-In-File([string]$varName, [string]$file) {
$regex = "(?<=\$varName = )[^`n]*"
$content = Get-Content -Raw $file
return [regex]::Match($content, $regex).Value
}
$file = 'C:\Users\v-anad\Documents\TestFolder\TestVars.ps1'
$var = '$var1'
$value = Get-Var-Value-In-File $var $file
$regex = "\$[^\\]*"
$nextVar = [regex]::Match($value, $regex).Value
$nextValue = Get-Var-Value-In-File $nextVar $file
Write-Output "$var = $value"
Write-Output "$nextVar = $nextValue"
Write-Output $nextVar.Replace($nextVar, $nextValue)
Write-Output ($value -replace [regex]::Escape($nextVar),$nextValue)
Output:
$var1 = "$var0\TestFolder"
$var0 = "C:\Users\v-anad\Documents"
"C:\Users\v-anad\Documents"
\TestFolder"-anad\Documents"
Note how the code above does not account for the extra quotes that would be inserted into the final value, so should this curious behavior be fixed, the output will be: ""C:\Users\v-anad\Documents"\TestFolder\"
The problem was there was a carriage return (\r, or `r in PowerShell) that my regex included in the match, causing the behavior when the string replacement occurred. Thanks to PerSerAl for being a second pair of eyes to catch it.

Grab parameters from url and drop them to powershell variables

i have a powershell listener running on my windows-box. Code from Powershell-Gallery: https://www.powershellgallery.com/packages/HttpListener/1.0.2
The "Client" calls the listener with the following example url:
With Powershell i do:
invoke-webrequest -Uri "http://127.0.0.1:8888/Test?id='1234'&param2='##$$'&param3='This is a test!'"
I have no idea, how to drop the parameters from the url to variables with the same name in powershell. I need to bring the parameters to powershellvariables to simply echo them. this is my last missing part. The parameters are separated with & and parameternames are case-sensitive.
To be more detailed, the id from the url should be in a powershell-variable named $id with the value 1234. The Variables can contain spaces, special characters, numbers, alphas. They are case sensitive. The parametervalue could be "My Name is "Anna"! My Pets Name is 'Bello'. " with all the "dirty" Characters like '%$"!{[().
Can someone point me to the right way how to get this solved?
In a valid url, the $ characters should have been escaped to %24
The other 'dirty' character % in Url escaped form is %25
This means the example url is invalid and should be:
$url = "http://127.0.0.1:8888/Test?id='1234'&param2='##%24%24'&param3='This is a test!'"
Then the following does work
$url = "http://127.0.0.1:8888/Test?id='1234'&param2='##%24%24'&param3='This is a test!'"
if ($url -is [uri]) {
$url = $url.ToString()
}
# test if the url has a query string
if ($url.IndexOf('?') -ge 0) {
# get the part of the url after the question mark to get the query string
$query = ($url -split '\?')[1]
# or use: $query = $url.Substring($url.IndexOf('?') + 1)
# remove possible fragment part of the query string
$query = $query.Split('#')[0]
# detect variable names and their values in the query string
foreach ($q in ($query -split '&')) {
$kv = $($q + '=') -split '='
$varName = [uri]::UnescapeDataString($kv[0]).Trim()
$varValue = [uri]::UnescapeDataString($kv[1])
New-Variable -Name $varname -Value $varValue -Force
}
}
else {
Write-Warning "No query string found as part of the given URL"
}
Prove it by writing the newly created variables to the console
Write-Host "`$id = $id"
Write-Host "`$param2 = $param2"
Write-Host "`$param3 = $param3"
which in this example would print
$id = '1234'
$param2 = '##$$'
$param3 = 'This is a test!'
However, I personally would not like to create variables like this, because of the risk of overwriting already existing ones.
I think it would be better to store them in a hash like this:
# detect variable names and their values in the query string
# and store them in a Hashtable
$queryHash = #{}
foreach ($q in ($query -split '&')) {
$kv = $($q + '=') -split '='
$name = [uri]::UnescapeDataString($kv[0]).Trim()
$queryHash[$name] = [uri]::UnescapeDataString($kv[1])
}
$queryHash
which outputs
Name Value
---- -----
id '1234'
param2 '##$$'
param3 'This is a test!'