Einzelnen Beitrag anzeigen

Benutzerbild von Remko

Registriert seit: 10. Okt 2006
Ort: 's-Hertogenbosch, Die Niederlande
220 Beiträge
RAD-Studio 2010 Arc

Password hash in RDP files

  Alt 20. Mär 2007, 09:35
Note: this topic is followup off a previous topic: Base64 encoding
With MSTSC.EXE you can save connection settings in an RDP file. This file will then also contain an hashed or encrypted password which is valid only for the user who created the RDP file. Analysis of MSTSC learns that the hash is created using MSDN-Library durchsuchenCryptProtectData API. The encrypted password is a fixed size (always 1329 bytes) and contains only HEX chars. And might look like this:
password 51:b:01000000D08C9DDF0115D1118C7A00C04FC297EB0100000062B
(removed the rest, you get the picture)

With the code below I'm able to produce a working RDP file with some remarks: The produced hash is smaller in size (about 350 bytes) and seems to work only during the same logon session.
So the question is, how to produce the same hash as MSTSC?
In the previous topic Marabu suggested the MSDN-Library durchsuchenCryptStringToBinary API. I also saw on MSDN there's a MSDN-Library durchsuchenCryptBinaryToString API. I've added Delphi translations for these functions to JwaCrypt CVS version.

Uses JwaCrypt;

var DataIn: DATA_BLOB;
    DataOut: DATA_BLOB;
    Description: PWideChar;
    Hash2: String;
    I: Integer;
    P: PByte;
    sRDPFileName: string;
    RDPFile: TStringList;
    LocalAppData: PAnsiChar;
    sFolder: string;
    pwCmdLine: PWideChar;
    pchString: DWord;
    Res: Boolean;
    pszString: PWideChar;

  GetMem(LocalAppData, MAX_PATH);
  if ShGetFolderPath(THandle(nil),
                     LocalAppData) = S_OK then
    sRDPFileName := String(LocalAppData + '\GPRMC');

    if not DirectoryExists(sRDPFileName) then

    sRDPFileName := sRDPFileName + '\GPRMC.rdp';

  DataOut.cbData := 0;
  DataOut.pbData := nil;
  DataIn.pbData := Pointer(WideString(Edit1.Text));
  DataIn.cbData := Length(Edit1.Text) * SizeOf(WChar);
  Description := WideString('psw');
  if CryptProtectData(@DataIn,
                      @DataOut) then
    P := DataOut.pbData;
    I := DataOut.cbData;

    Hash2 := '';
    while (I > 0) do begin
      Hash2 := Hash2 + IntToHex(P^, 2);

    RDPFile := TStringList.Create;
    RDPFile.Add('screen mode id:i:1');
    RDPFile.Add(Format('desktopwidth:i:%d', [Screen.Width]));
    RDPFile.Add(Format('desktopheight:i:%d', [Screen.Height - 50]));
    RDPFile.Add('session bpp:i:16');
    RDPFile.Add('full address:s:SERVERNAME');
    RDPFile.Add('autoreconnection enabled:i:1');
    RDPFile.Add('alternate shell:s:');
    RDPFile.Add('shell working directory:s:');
    RDPFile.Add('password 51:b:' + Hash2);
    RDPFile.Add('disable wallpaper:i:1');
    RDPFile.Add('disable full window drag:i:1');
    RDPFile.Add('disable menu anims:i:1');
    RDPFile.Add('disable themes:i:1');
    RDPFile.Add('disable cursor setting:i:0');

    ZeroMemory(@si, SizeOf(si));
    si.cb := SizeOf(si);
    si.lpDesktop := nil;
    pwCmdLine := PWideChar(WideString('mstsc.exe ' + '"' + sRDPFileName + '"'));
    if CreateProcessW(nil,
                      pi) then
      // Succes
    else begin
      // Failure
Debugging MSTSC.EXE while saving an RDP shows this sequence:

CryptProtectData - CRYPT32.dll
CryptUnprotectData - CRYPT32.dll
CryptUnprotectData - CRYPT32.dll
CryptProtectData - CRYPT32.dll

It seems like the first CryptProtectData crypts the username (I passed Username as user)
And the 2nd CryptProtectData the Password (I passed Password as the password string)
  Mit Zitat antworten Zitat