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
CryptProtectData API. The encrypted password is a fixed size (always 1329 bytes) and contains only HEX chars. And might look like this:
Code:
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
CryptStringToBinary API. I also saw on
MSDN there's a
CryptBinaryToString API. I've added Delphi translations for these functions to
JwaCrypt CVS version.
Delphi-Quellcode:
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;
si: _STARTUPINFOW;
pi: PROCESS_INFORMATION;
pwCmdLine: PWideChar;
pchString: DWord;
Res: Boolean;
pszString: PWideChar;
begin
GetMem(LocalAppData, MAX_PATH);
if ShGetFolderPath(THandle(
nil),
CSIDL_LOCAL_APPDATA,
THandle(
nil),
SHGFP_TYPE_CURRENT,
LocalAppData) = S_OK
then
begin
sRDPFileName :=
String(LocalAppData + '
\GPRMC');
if not DirectoryExists(sRDPFileName)
then
begin
MkDir(sRDPFileName);
end;
sRDPFileName := sRDPFileName + '
\GPRMC.rdp';
Memo1.Lines.Add(sRDPFileName);
end;
FreeMem(LocalAppData);
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,
Description,
nil,
nil,
nil,
CRYPTPROTECT_UI_FORBIDDEN,
@DataOut)
then
begin
P := DataOut.pbData;
I := DataOut.cbData;
Hash2 := '
';
while (I > 0)
do begin
Dec(I);
Hash2 := Hash2 + IntToHex(P^, 2);
Inc(P);
end;
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('
winposstr:s:2,3,247,0,1055,627');
RDPFile.Add('
full address:s:SERVERNAME');
RDPFile.Add('
compression:i:1');
RDPFile.Add('
keyboardhook:i:2');
RDPFile.Add('
audiomode:i:2');
RDPFile.Add('
redirectdrives:i:0');
RDPFile.Add('
redirectprinters:i:0');
RDPFile.Add('
redirectcomports:i:0');
RDPFile.Add('
redirectsmartcards:i:0');
RDPFile.Add('
displayconnectionbar:i:1');
RDPFile.Add('
autoreconnection enabled:i:1');
RDPFile.Add('
username:s:USERNAME');
RDPFile.Add('
domain:s:DOMAIN');
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');
RDPFile.Add('
bitmapcachepersistenable:i:1');
RDPFile.SaveToFile(sRDPFileName);
RDPFile.Free;
ZeroMemory(@si, SizeOf(si));
si.cb := SizeOf(si);
si.lpDesktop :=
nil;
pwCmdLine := PWideChar(WideString('
mstsc.exe ' + '
"' + sRDPFileName + '
"'));
if CreateProcessW(
nil,
pwCmdLine,
nil,
nil,
False,
0,
nil,
nil,
si,
pi)
then
begin
// Succes
end
else begin
// Failure
end;
end;
end;
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)
http://web.inter.nl.net/users/weijnen/dp/Info1.jpg
And the 2nd CryptProtectData the Password (I passed Password as the password string)
http://web.inter.nl.net/users/weijnen/dp/Info2.jpg