From d588a8a5bf05cb0b6fcf6fcff4bc96ccca822580 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano-Tarumi?= Date: Wed, 28 Oct 2020 09:55:47 +0100 Subject: [PATCH 1/4] Prepend the user script with a UTF-8 BOM --- lib/winrm/shells/elevated.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/winrm/shells/elevated.rb b/lib/winrm/shells/elevated.rb index 29aa780..bac7537 100644 --- a/lib/winrm/shells/elevated.rb +++ b/lib/winrm/shells/elevated.rb @@ -73,7 +73,9 @@ def close def upload_elevated_shell_script(script_text) elevated_shell_path = 'c:/windows/temp/winrm-elevated-shell-' + SecureRandom.uuid + '.ps1' - script_text_with_exit = "#{script_text}\r\n$Host.SetShouldExit($LASTEXITCODE)" + # Prepend the content of the file with an UTF-8 BOM for Windows to read it as such instead of the default + # Windows-XXXX encoding, and convert script_text accordingly if needed. + script_text_with_exit = "\uFEFF#{script_text.encode(Encoding::UTF_8)}\r\n$Host.SetShouldExit($LASTEXITCODE)" @winrm_file_transporter.upload(StringIO.new(script_text_with_exit), elevated_shell_path) elevated_shell_path end From 78346d175628d64a07ab678ed2a5a4d7c60c485d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano-Tarumi?= Date: Wed, 28 Oct 2020 12:16:00 +0100 Subject: [PATCH 2/4] Read output as OEM --- lib/winrm-elevated/scripts/elevated_shell.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/winrm-elevated/scripts/elevated_shell.ps1 b/lib/winrm-elevated/scripts/elevated_shell.ps1 index cd95618..61f0eb1 100644 --- a/lib/winrm-elevated/scripts/elevated_shell.ps1 +++ b/lib/winrm-elevated/scripts/elevated_shell.ps1 @@ -88,7 +88,7 @@ try { function SlurpOutput($file, $cur_line, $out_type) { if (Test-Path $file) { - get-content $file | Select-Object -skip $cur_line | ForEach-Object { + get-content -Encoding Oem $file | Select-Object -skip $cur_line | ForEach-Object { $cur_line += 1 if ($out_type -eq 'err') { $host.ui.WriteErrorLine("$_") From 5e79eb6f089e6fd4bddd161c4f5bddbd72ba4af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano-Tarumi?= Date: Wed, 28 Oct 2020 10:10:12 +0100 Subject: [PATCH 3/4] Test special characters handling --- spec/powershell_elevated_spec.rb | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spec/powershell_elevated_spec.rb b/spec/powershell_elevated_spec.rb index 85094fd..b613a2b 100644 --- a/spec/powershell_elevated_spec.rb +++ b/spec/powershell_elevated_spec.rb @@ -54,6 +54,22 @@ it { should match(/Windows IP Configuration/) } end + describe 'special characters' do + subject(:output) { elevated_shell.run("echo \"#{text}\"") } + # Sample text using more than ASCII, but still compatible with occidental OEM encodings. + let(:text) do + 'Dès Noël, où un zéphyr haï me vêt de glaçons würmiens, je dîne d’exquis rôtis de bœuf au kir, ' \ + 'à l’aÿ d’âge mûr, &cætera.' + end + + it { should have_exit_code 0 } + it 'outputs a transliterated version of the original string' do + expect(output.stdout).to eq "Dès Noël, où un zéphyr haï me vêt de glaçons würmiens, je dîne d'exquis " \ + "rôtis de bouf au kir, à l'aÿ d'âge mûr, &cætera.\r\n" + end + it { should have_no_stderr } + end + describe 'capturing output from Write-Host and Write-Error' do subject(:output) do script = <<-COMMAND From 6153de4ff5352e6e40f56062f9e78c5926d15059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Mangano-Tarumi?= Date: Fri, 6 Nov 2020 10:03:01 +0100 Subject: [PATCH 4/4] Replace Get-Content -Encoding Oem by a PowerShell 2-compatible equivalent Courtesy of Matt Wrock. --- lib/winrm-elevated/scripts/elevated_shell.ps1 | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/lib/winrm-elevated/scripts/elevated_shell.ps1 b/lib/winrm-elevated/scripts/elevated_shell.ps1 index 61f0eb1..d297879 100644 --- a/lib/winrm-elevated/scripts/elevated_shell.ps1 +++ b/lib/winrm-elevated/scripts/elevated_shell.ps1 @@ -88,14 +88,28 @@ try { function SlurpOutput($file, $cur_line, $out_type) { if (Test-Path $file) { - get-content -Encoding Oem $file | Select-Object -skip $cur_line | ForEach-Object { - $cur_line += 1 - if ($out_type -eq 'err') { - $host.ui.WriteErrorLine("$_") - } else { - $host.ui.WriteLine("$_") + $fs = New-Object -TypeName System.IO.FileStream -ArgumentList @( + $file, + [system.io.filemode]::Open, + [System.io.FileAccess]::Read, + [System.IO.FileShare]::ReadWrite + ) + try { + $enc = [System.Text.Encoding]::GetEncoding($Host.CurrentCulture.TextInfo.OEMCodePage) + $bytes = [System.Byte[]]::CreateInstance([System.Byte], $fs.Length) + if ($fs.Read($bytes, 0, $fs.Length) -gt 0) { + $text = $enc.GetString($bytes) + $text.TrimEnd("`n").TrimEnd("`r").Split(@("`r`n", "`n"), [StringSplitOptions]::None) | Select-Object -skip $cur_line | ForEach-Object { + $cur_line += 1 + if ($out_type -eq 'err') { + $host.ui.WriteErrorLine("$_") + } else { + $host.ui.WriteLine("$_") + } + } } } + finally { $fs.Close() } } return $cur_line }