When creating an encrypted VM, VMware Workstation gives you the option to remember the password. It does this by storing the password in the Windows Credential Manager.
VMware does not provide a way to retrieve this stored password, but it can be accessed via the Win32 CredReadW API function.
There are a number of PowerShell projects including PowerShell Credential Manager which provide access to this API, but in testing I found they were unable to correctly display the VMware password.
This PowerShell example has been tested using Windows PowerShell (v5.1) and PowerShell (v7) using VMware Workstation 17.5 on Windows 11 23H2. Windows Terminal is used as the host for command line apps. It has been written with the assistance of ChatGPT.
Open the .vmx
file for your VM and look for encryptedVM.guid
. This is the name of the credential within Windows Credential Manager. For example:
encryptedVM.guid = "{833AB4F5-587E-4B11-9260-4DB13742FA7F}"
Open PowerShell or Windows PowerShell, copy and paste the below code. This provides an interface to the Win32 API via C#.
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Win32Cred
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CREDENTIAL
{
public int Flags;
public int Type;
public IntPtr TargetName;
public IntPtr Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public int CredentialBlobSize;
public IntPtr CredentialBlob;
public int Persist;
public int AttributeCount;
public IntPtr Attributes;
public IntPtr TargetAlias;
public IntPtr UserName;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredReadW(string target, int type, int reservedFlag, out IntPtr credentialPtr);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern void CredFree(IntPtr buffer);
public static string CredRead(string targetName, int type = 1)
{
IntPtr credPtr;
if (CredReadW(targetName, type, 0, out credPtr))
{
CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(CREDENTIAL));
string pass = Marshal.PtrToStringAnsi(cred.CredentialBlob, cred.CredentialBlobSize);
CredFree(credPtr);
return pass;
}
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
}
"@
Run the following command replacing {encryptedVM.guid}
with the GUID identified in step 1:
[Win32Cred]::CredRead("{encryptedVM.guid}")
For example:
[Win32Cred]::CredRead("{833AB4F5-587E-4B11-9260-4DB13742FA7F}")
The password should be output to the screen:
PS C:\Users\Andrew [Win32Cred]::CredRead("{833AB4F5-587E-4B11-9260-4DB13742FA7F}")
MySecretPassword
In this example I use Marshal.PtrToStringAnsi
to convert the pointer to the password into a string.
This works in both Windows PowerShell and PowerShell, as it is a C# method available in both .NET Framework and modern .NET (Core).
Marshal.PtrToStringUni
does not seem to work, but Marshal.PtrToStringUTF8
does and may be more flexible. This would require the use of PowerShell as this method is only implemented in modern .NET.
It may be preferable to use this, as not all characters will display using ANSI. For example, say your password was made up of emoji characters. Using the above example with Marshal.PtrToStringAnsi
the password is going to be output as incorrect characters:
But using Marshal.PtrToStringUTF8
the password will be correctly displayed, and can be copied/pasted as required:
So if this doesn't work for you, you may want to try swapping these functions in the above code, from:
string pass = Marshal.PtrToStringAnsi(cred.CredentialBlob, cred.CredentialBlobSize);
To:
string pass = Marshal.PtrToStringUTF8(cred.CredentialBlob, cred.CredentialBlobSize);
Remember, you must use PowerShell and not Windows PowerShell when using this method.
Here is the full code example using Marshal.PtrToStringUTF8
:
Add-Type @"
using System;
using System.Runtime.InteropServices;
public class Win32Cred
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct CREDENTIAL
{
public int Flags;
public int Type;
public IntPtr TargetName;
public IntPtr Comment;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWritten;
public int CredentialBlobSize;
public IntPtr CredentialBlob;
public int Persist;
public int AttributeCount;
public IntPtr Attributes;
public IntPtr TargetAlias;
public IntPtr UserName;
}
[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool CredReadW(string target, int type, int reservedFlag, out IntPtr credentialPtr);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern void CredFree(IntPtr buffer);
public static string CredRead(string targetName, int type = 1)
{
IntPtr credPtr;
if (CredReadW(targetName, type, 0, out credPtr))
{
CREDENTIAL cred = (CREDENTIAL)Marshal.PtrToStructure(credPtr, typeof(CREDENTIAL));
string pass = Marshal.PtrToStringUTF8(cred.CredentialBlob, cred.CredentialBlobSize);
CredFree(credPtr);
return pass;
}
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
}
}
"@
You then invoke it in the same was as in step 2.