Utility for non-admin human shadow connection to RDP user sessions in WinServer 2012R2

The problem during the period of quarantine work of the enterprise became the following: it is really necessary to minimize the number of visits to offices by specialists servicing and advising on application software, and frankly, users often abuse the help of specialists not wanting to delve into the question itself, they say β€œthey will come - they will help - they will do it, but I'll smoke / drink coffee, etc. for now. Phone consultation when sharing a server is more effective when viewing a remote screen.

Utility for non-admin human shadow connection to RDP user sessions in WinServer 2012R2

Already after the β€œinvention” of our bicycle, sane information on the topic of the article turned up: RDS Shadow - shadow connection to RDP user sessions in Windows Server 2012 R2 or Unprivileged user shadow mode in windows server or Delegate management of RDP sessions. All of them involve using the console, even with simple dialog elements.

All information below is intended for those who normally tolerates abnormal perversions to get the desired result, inventing unnecessary methods.
In order not to β€œpull the cat by the tail”, I’ll start with the last one: the bike works for a regular user using the utility AdmiLink, for which its author and thanks.

I. Console and shadow RDP.

Since use with admin rights of the Server Manager console -> QuickSessionCollection -> by clicking on the session of the user of interest, selecting Shadow from the context menu for personnel instructing how to work with software, - not an option, another "wooden" method was considered, namely:

1. Find out the RDP id of the session:

query user | findstr Administrator

or:

qwinsta | findstr Administrator 

And "| findstr Administrator"It was only convenient when you know exactly what Administrator you need, or use only the first part to see all logged in on the server.

Utility for non-admin human shadow connection to RDP user sessions in WinServer 2012R2

2. We connect to this session, provided that in the domain group policies "Sets remote control rules for user Remote Desktop Services sessions" option is set to at least "Monitor session with user permission" (more):

mstsc /shadow:127

Please note that only user logins will be in the list.

I repeat that without admin rights you will get the following:

Utility for non-admin human shadow connection to RDP user sessions in WinServer 2012R2

But for preliminary debugging of the program, which will be discussed, I used an account with administrator rights.

II. Program

So the problem statement: creating some simple graphical interface for connecting to the user's shadow sense with his permission, sending a message to the user. Programming environment chosen by Lazarus.

1. We get the full domain list of users "login" - "full name" from the admin, or again through the console:

wmic useraccount get Name,FullName 

no one forbids even this:

wmic useraccount get Name,FullName > c:testusername.txt

I will say right away that it was Lazarus who had a problem with processing this file, since by default its encoding is UCS-2, so I just had to manually convert it to regular UTF-8. There are a lot of tabs in the file structure, or rather a lot of spaces, which it was decided to process programmatically, sooner or later the encoding problem will be solved, and the file will be programmatically updated.

So, the idea is a folder accessible to users of the program, for example c: test, in which there will be 2 files: the first with login and fullname, the second with id_rdp and login users. Further, we process this data as best we can :).

In the meantime, to associate with the list of sessions, we transfer this (login and fullname) contents to an array:

procedure Tf_rdp.UserF2Array;
var 
  F:TextFile;   i:integer;   f1, line1:String;   fL: TStringList;
begin //f_d Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ ΠΏΡƒΡ‚ΡŒ ΠΊ Ρ€Π°Π·ΠΌΠ΅Ρ‰Π΅Π½ΠΈΡŽ Ρ„Π°ΠΉΠ»ΠΎΠ² 
f1:=f_d+'user_name.txt';     //Π·Π°Π΄Π°Ρ‡Π° ΡΡ‡ΠΈΡ‚Π°Ρ‚ΡŒ Π² массив содСрТимоС Ρ„Π°ΠΉΠ»Π°
fL := TStringList.Create; // строку ΠΏΠΎΠ΄Π²Π΅Ρ€Π³Π½Π΅ΠΌ ΠΌΠ΅Ρ‚Π°ΠΌΠ°Ρ€Ρ„ΠΎΠ·Π°ΠΌ с раздСлитСлями
fL.Delimiter := '|'; fL.StrictDelimiter := True;
AssignFile(F,f1); 
try // ΠžΡ‚ΠΊΡ€Ρ‹Ρ‚ΡŒ Ρ„Π°ΠΉΠ» для чтСния
  reset(F); ReadLn(F,line1);
  i:=0;
while not eof(F) do // Π‘Ρ‡ΠΈΡ‚Ρ‹Π²Π°Π΅ΠΌ строки, ΠΏΠΎΠΊΠ° Π½Π΅ закончится Ρ„Π°ΠΉΠ»
begin
ReadLn(F,line1);
line1:= StringReplace(line1, '  ', '|',[]); //замСняСм ΠΏΠ΅Ρ€Π²Ρ‹ΠΉ попавш.2ΠΏΡ€ΠΎΠ±Π΅Π»Π° Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚Π΅Π»Π΅ΠΌ |
// удаляСм всС Π΄Π²ΠΎΠΉΠ½Ρ‹Π΅ ΠΏΡ€ΠΎΠ±Π΅Π»Ρ‹
while pos('  ',line1)>0 do line1:= StringReplace(line1, '  ', ' ', [rfReplaceAll]);
begin
if (pos('|',line1)>0) then
begin //Ссли Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚Π΅Π»ΡŒ сущСствуСт заносим Π΅Π³ΠΎ Π² массив
fL.DelimitedText :=line1; // Ρ€Π°Π·Π±ΠΈΠ²Π°Π΅ΠΌ Π½Π° столбцы
if (fL[0]<>'') then //Ссли ΡƒΡ‡Π΅Ρ‚ΠΊΠ° ΠΈΠΌΠ΅Π΅Ρ‚ имя
begin //вносим СС в массив
 inc(i); // избавляСмся ΠΎΡ‚ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹Ρ… ΠΎΠ΄ΠΈΠ½ΠΎΡ‡Π½Ρ‹Ρ… ΠΏΡ€ΠΎΠ±Π΅Π»ΠΎΠ² Π² Π»ΠΎΠ³ΠΈΠ½Π΅
 fam[0,i]:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]);
 fam[1,i]:=fL[0];
 end;end;end;end; // Π“ΠΎΡ‚ΠΎΠ²ΠΎ. Π—Π°ΠΊΡ€Ρ‹Π²Π°Π΅ΠΌ Ρ„Π°ΠΉΠ».
 CloseFile(F);
 Fl.Free;
 except
 on E: EInOutError do  ShowMessage('Ошибка ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ Ρ„Π°ΠΉΠ»Π°. Π”Π΅Ρ‚Π°Π»ΠΈ: '+E.Message);
 end;end;

I apologize for "a lot of code", the following points will be more concise.

2. Similarly to the method from the previous paragraph, we read the result of processing the list into the StringGrid element, while I will give a β€œsignificant” piece of code:

2.1 Get the current list of RDP sessions to a file:

f1:=f_d+'user.txt';
cmdline:='/c query user >'+ f1;
if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1)=0 then;
Sleep(500); // ΠΌΠΎΠΆΠ½ΠΎ ΠΈ подольшС ΠΆΠ΄Π°Ρ‚ΡŒ ΠΏΠΎΠΊΠ° Ρ„Π°ΠΉΠ» для чтСния создаСтся

2.2 We process the file (only significant lines of code are indicated):

StringGrid1.Cells[0,i]:=fL[1]; StringGrid1.Cells[2,i]:=fL[3]; //ΠΊΠΈΠ΄Π°Π΅ΠΌ Π² Ρ†ΠΈΠΊΠ»Π΅ Π² StringGrid1
login1:=StringReplace(fL[1],' ','',[rfReplaceall, rfIgnoreCase]); //ΡƒΠ±ΠΈΡ€Π°Π΅ΠΌ ΠΈΠ· Π»ΠΎΠ³ΠΈΠ½Π° ΠΏΡ€ΠΎΠ±Π΅Π»Ρ‹
if (SearchArr(login1)>=0) then //ΠΈΡ‰Π΅ΠΌ Π² массивС ΠΈΠ· ΠΏ1. Π»ΠΎΠ³ΠΈΠ½ ΠΈ записываСм Π² Ρ‚Π°Π±Π»ΠΈΡ†Ρƒ ЀИО
StringGrid1.Cells[1,i]:=fam[1,SearchArr(login1)]
else StringGrid1.Cells[1,i]:='+'; // Π»ΠΈΠ±ΠΎ записываСм плюсик:)
.... //Π² зависимости ΠΎΡ‚ Π²Ρ‹Π±ΠΎΡ€Π° ΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚Π΅Π»Ρ сортируСм ΠΈ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ ΠΏΠΎ Π΄Π°Π½Π½Ρ‹ΠΌ
if (b_id.Checked=true) then SortGrid(0) else SortGrid(1);
StringGrid1.AutoSizeColumn(0);StringGrid1.AutoSizeColumn(1); StringGrid1.AutoSizeColumn(2);  

3. Direct connection itself when clicking on the line with the user and his session number:

  id:=(StringGrid1.Row);// ΡƒΠ·Π½Π°Π΅ΠΌ Π½ΠΎΠΌΠ΅Ρ€ строки  IntToStr(StringGrid1.Row)
  ids:=StringGrid1.Cells[2,id]; //ΠΏΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ rdp
  cmdline:='/c mstsc /shadow:'+ ids; //ΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Π΅ΠΌΡΡ....
 if (b_rdp.Checked=True) then  if ShellExecute(0,nil, PChar('cmd'),PChar(cmdline),nil,1) =0 then;       

4. A couple more decorations were made, such as sorting by clicking on the radiobutton, and messages to the user, or to all users.

Utility for non-admin human shadow connection to RDP user sessions in WinServer 2012R2

β†’ Full source code can be seen here

III. Using AdminLink - what I saw:

AdminLink does indeed generate a shortcut that links to the location of the utility admilaunch.exe, and a personal copy of the launch utility AdmiRun.exe which is located in the user folder, for example vasya, type C:UsersvasyaWINDOWS. In general, not everything is so bad: you can play around with the access rights to the shortcut file and others to clear your own admin conscience.

Source: habr.com

Add a comment