RDCManのrdgファイルからパスワードを取り出す

`Sysinternals`で配布されている`Remote Desktop Connection Manager`。

こちらのソフトウェアについて、一時期配布が取りやめになっていましたがまさかの復活を遂げています。

こちらのソフトウェアについて、接続情報を`.rdg`ファイルとして保存しておりますが。
今回は`.rdg`ファイルから設定を読み取り、パスワードを復号する方法を説明します。

なお本手順で説明する復号化については、`rdgファイル`を作成したユーザ ＆　Windows環境のPowerShell でないと復号はできないかと思います。

## ドキュメント・参考サイト

- [Sysinternals](https://docs.microsoft.com/ja-jp/sysinternals/)
- [Sysinternals Remote Desktop Connection Manager](https://docs.microsoft.com/en-us/sysinternals/downloads/rdcman)
- [DECRYPTING REMOTE DESKTOP CONNECTION MANAGER PASSWORDS WITH POWERSHELL](https://smsagent.blog/2017/01/26/decrypting-remote-desktop-connection-manager-passwords-with-powershell/)
- [https://www.pdq.com/blog/rdcman-file-powershell/](https://www.pdq.com/blog/rdcman-file-powershell/)

## 復号化

`.rdg`ファイルは実体としてはxmlファイルとなっており、`password`ノードに暗号化されて保存されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/b69dee32-1b17-efc9-3696-97745cfcf879.png)

このpsswordノードに記載されている文字列はRDCMan.exeを`PowerShell`の`Import-Module <<rdcman.exeパス>>`とすると`[RdcMan.Encryption]`がインポート出来るので。
※`WindowsPowerShell`では拡張子.exeをImport-Moduleで読めないので。RDCMan.exeをRDCMan.dllに変更して読み込んで下さい。

ここに用意されている`DecryptString`メソッドを使うと復号化できるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1670622/873a1415-70a1-f8e9-7d62-18350d2dd5be.png)


```PowerShell
# RDCMan.exeをdllに変更してImport-Module
Import-Module .\RDCMan.dll

# DecryptStringメソッドで復号化
[RdcMan.Encryption]::DecryptString( <<<passwordnoteの文字列>>>, $(New-Object -TypeName RdcMan.EncryptionSettings))
```

## rdgファイルを読み込むサンプル

`Inherit from parent`で資格情報を参照設定している物については未対応ですが、`RDCMan.exe`が存在するディレクトリに下記スクリプト配置して実行すると情報を一覧で出力するサンプルとなります。

```PowerShell:Convert-FromRDGFile.ps1
#!/usr/bin/env pwsh
<#
.SYNOPSIS
    rdgファイルから設定情報を抽出
.DESCRIPTION
    rdgファイルから設定情報を抽出
    rdcman.exeを配置しているディレクトリに配置して実行して下さい
    Inherit from parentの設定方法には未対応
.EXAMPLE
    PS C:\> ./Convert-FromRDGFile.ps1 -Path <<RDG File>>
    Explanation of what the example does
.INPUTS
    none
.OUTPUTS
    Output (if any)
.NOTES
    
#>

[CmdletBinding()]
param (
    [Parameter(Mandatory = $true)]
    [string]$RDGFilePath
)

PROCESS {

    Set-StrictMode -Version Latest
    $ErrorActionPreference = "stop"

    # RDCman.exeをインポート
    # WindowsPowerShellでは拡張子exeは読み込めないためdllとしてコピー
    if (-not (Test-Path -Path "$PSScriptRoot\RDCMan.dll")){
        Copy-Item -path "$PSScriptRoot\RDCMan.exe" -Destination "$PSScriptRoot\RDCMan.dll" -Force
    }
    Import-Module "$PSScriptRoot\RDCMan.dll" -Force
    $EncryptionSettings = New-Object -TypeName RdcMan.EncryptionSettings

    # rdgファイルを読み込み
    [xml]$rdgFile = Get-Content -Path $RDGFilePath

    $servers = Select-Xml -Xml $rdgFile -XPath '//server' | Select-Object -ExpandProperty Node

    foreach ($serverNode in $servers) {
        $outputRow = [PSCustomObject]@{
            scope          = ""
            displayName    = ""
            name           = ""
            userName       = ""
            domain         = ""
            passwordString = ""
        }

        # displayNameが存在するかチェック
        if ([boolean]($serverNode.properties | Get-Member DisplayName)) {
            $outputRow.displayName = $serverNode.properties.displayName
        }
        $outputRow.name = $serverNode.properties.Name

        # logonCredentialsが登録なければループを飛ばす
        if (-not [boolean]($serverNode | Get-Member logonCredentials)) {
            write-output $outputRow
            continue
        }
        switch ($serverNode.logonCredentials.profileName.scope) {
            "Local" {
                $outputRow.scope = "Local"
                $outputRow.userName = $serverNode.logonCredentials.userName
                $outputRow.domain = $serverNode.logonCredentials.domain
                $outputRow.passwordString = [RdcMan.Encryption]::DecryptString($serverNode.logonCredentials.password, $EncryptionSettings)
            }
            "File" {
                $outputRow.scope = "File"
                $XPATH = "//credentialsProfile[profileName='{0}']" -f $serverNode.logonCredentials.profileName."#text"
                $credentialsProfile = Select-Xml -Xml $rdgFile -XPath $XPATH
                $outputRow.userName = $credentialsProfile.Node.userName
                $outputRow.domain = $credentialsProfile.Node.userName
                $outputRow.passwordString = [RdcMan.Encryption]::DecryptString($credentialsProfile.Node.password, $EncryptionSettings)
            }
            Default {}
        }
        write-output $outputRow
    }

}
```

## 総評

使う機会はないような気がしますが、こういう方法があるんですね。　といった感あります。