Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi WinStationShadow API Function (https://www.delphipraxis.net/81827-winstationshadow-api-function.html)

Remko 4. Dez 2006 16:01


WinStationShadow API Function
 
Sorry for writing in English, I can read and write some German but its not sufficient to describe my question.
I'm looking for any info on how to use the WinStationShadow function from Winsta.dll to incorporate an option to shadow a users sessions from my app. Currently I use shadow.exe with commandline params but this isn't really nice.
Both tsadmin.exe and shadow.exe seem to use winsta.dll functions WinStationShadow and WinStationShadowStop which presumably use 2 structures _WinStationShadowTarget and/or _WinStationShadowTargetSetup.

Luckie 4. Dez 2006 17:55

Re: WinStationShadow API Function
 
Maybe this can help you: http://www.michael-puff.de/dirindex..../Importe/Nico/ -> winwlx.zip "Interface for the Microsoft WinLogon eXtention (WLX) API for Delphi"
If that doen't help try to contact Olli. He might be the only person who can help you.

Remko 4. Dez 2006 18:59

Re: WinStationShadow API Function
 
WinWlx doesn't contain the function or any pointers in the right direction. I agree with you that Olli could probably help, but I already asked him ;-) and he's very busy right now. He helped me before with other functions from Winsta.dll to get idle and login time from terminal server sessions.

Remko 11. Dez 2006 13:35

Re: WinStationShadow API Function
 
I did find out some more, just posting it here to share my information. Perhaps someone knows the next step?
Starting a Remote Control from TSAdmin.exe, accepting this from the client and exiting remote control executes these API functions:

Code:
API Name                    Return Value        Module Name
WinStationOpenServerW       808544 (0xC5660)    WINSTA.dll
Before Call Parameters
Pointer Paramter0: 2542716 (0x26CC7C)
Pointer Paramter1: (null)
Pointer Paramter2: 12367680 (0xBCB740)
Pointer Paramter3: 2 (0x2)
Pointer Paramter4: 7 (0x7)
Pointer Paramter5: 1 (0x1)
After Call Parameters
Pointer Paramter0: 2542716 (0x26CC7C)
Pointer Paramter1: (null)
Pointer Paramter2: 12367680 (0xBCB740)
Pointer Paramter3: 2 (0x2)
Pointer Paramter4: 7 (0x7)
Pointer Paramter5: 1 (0x1)
Return
808544 (0xC5660)

API Name                    Return Value        Module Name
WinStationShadow            1 (0x1)             WINSTA.dll
Before Call Parameters
Pointer Paramter0: (null)                 <-------------- unknown
Pointer Paramter1: 2542716 (0x26CC7C)     <-------------- not sure, handle to server or some unknow structure? == Param0 from WinStationOpenServerW
Pointer Paramter2: 3 (0x3)                <-------------- sessionid
Pointer Paramter3: 106 (0x6A)             <-------------- keycode for stopping the remote control (VK_MULTIPLY = 6A)
Pointer Paramter4: 2 (0x2)                <-------------- plus key (shift=1, ctrl=2, alt =4)
Pointer Paramter5: (null)                 <-------------- unknown
After Call Parameters
Pointer Paramter0: (null)
Pointer Paramter1: 2542716 (0x26CC7C)
Pointer Paramter2: 3 (0x3)
Pointer Paramter3: 106 (0x6A)
Pointer Paramter4: 2 (0x2)
Pointer Paramter5: (null)
Return
1 (0x1)                                   <--------------- Boolean or error/result code?


WinStationQueryInformationW 1 (0x1)             WINSTA.dll
Before Call Parameters
Pointer Paramter0: 808544 (0xC5660)
Pointer Paramter1: 3 (0x3)
Pointer Paramter2: 8 (0x8)
Pointer Paramter3: 451472 (0x6E390)
Pointer Paramter4: 1216 (0x4C0)
Pointer Paramter5: 452704 (0x6E860)
After Call Parameters
Pointer Paramter0: 808544 (0xC5660)
Pointer Paramter1: 3 (0x3)
Pointer Paramter2: 8 (0x8)
Pointer Paramter3: 451472 (0x6E390)
Pointer Paramter4: 1216 (0x4C0)
Pointer Paramter5: 452704 (0x6E860)
Return
1 (0x1)

API Name                    Return Value        Module Name
WinStationCloseServer       1 (0x1)             WINSTA.dll
Before Call Parameters
Pointer Paramter0: 808544 (0xC5660)
Pointer Paramter1: 454720 (0x6F040)
Pointer Paramter2: 1701515922 (0x656B1292)
Pointer Paramter3: 808544 (0xC5660)
Pointer Paramter4: (null)
Pointer Paramter5: 12367680 (0xBCB740)
After Call Parameters
Pointer Paramter0: 808544 (0xC5660)
Pointer Paramter1: 454720 (0x6F040)
Pointer Paramter2: 1701515922 (0x656B1292)
Pointer Paramter3: 808544 (0xC5660)
Pointer Paramter4: (null)
Pointer Paramter5: 12367680 (0xBCB740)
Return
1 (0x1)

Remko 12. Dez 2006 15:25

Re: WinStationShadow API Function
 
Just posting my progress:
const SERVERNAME_CURRENT: HWND = THandle(Nil);
const LOGONID_CURRENT: ULONG = ULong(-1);

function WinStationShadow(hServer: Handle; ActiveSessionID: ULong; SessionID: ULONG; KeyCode: ULong; KeyModifier: ULong): Boolean; stdcall; // ActiveSessionID can be LOGONID_CURRENT, hServer can be LOGONID_CURRENT

function WinStationConnectW(hServer: HANDLE; ConnectSessionID: ULong; ActiveSessionID: ULong; pPassword: PWideChar; Unknown: ULong): boolean; //Unknown seems to be always 0

Remote Control is working, stopping it isn't working yet. Current thoughts on how to proceed:
use OnStateChangeEvent, when fired check state of shadowed session (not WTSShadow), if so use WinStationShadowStop function (parameters unknown).

Remko 18. Dez 2006 09:10

Re: WinStationShadow API Function
 
Liste der Anhänge anzeigen (Anzahl: 1)
Again updating some progress:
Got starting a remote control and ending it with the supplied hotkey working. See attachment for code.
Currently included functions:
  • WinStationConnect (connect to a disconnect session)
  • WinStationQueryInformation (get login and idle time from a session)
  • WinStationShadow (shadow another session)

Olli 19. Dez 2006 01:36

Re: WinStationShadow API Function
 
Nah, Remko. I told you the second parameter in WinStationShadow() must be a string (LPCWSTR or PWideChar respectively). So NULL (nil) will just be an empty string then ... no idea what should be the contents, though. But it's definitely a string param. Also remember I told you that WinStationShadowStop() takes 3 parameters (check your chat logs). I haven't found out what the third parameter is, though. Use ULONG and 0 now. But if you declare it with two parameters only your stack will be unbalanced after every call!!!!

Code:
BOOLEAN __stdcall WinStationShadow(HANDLE hServer, LPCWSTR lpwszUnknownString, ULONG SessionID, ULONG KeyCode, ULONG KeyModifier);
BOOLEAN __stdcall WinStationShadowStop(HANDLE hServer, ULONG SessionID, int);
-1 for the session ID will cause the function to retrieve the default value of the current process' WindowStation from Teb.Peb.Win32WindowStation

Code:
mov    eax, large fs:18h ; TEB
mov    eax, [eax+30h] ; TEB.Peb
mov    eax, [eax+1D4h] ; PEB.Win32WindowStation
NULL for the hServer parameter causes the function to call the (non-exported) function WinStationOpenLocalServer() and retrieve the handle to the local server.

:mrgreen: :zwinker:

Remko 19. Dez 2006 07:40

Re: WinStationShadow API Function
 
Olli,

I made a type in the mail I sent you, what I meant to say is:
I misinterpreted WinStationShadowStop, I was thinkin way too complicated. We don't need to watch for session state change event and call WinStationShadowStop manually. When you press the hotkey it's done for you. So this function is only there if you want to stop remote control programmatically (why would one want to do that, the app that started is invisible?).
When I pass a PWideChar as second parameter for WinStationShadow nothing happens (no remote control), when I pass ULONG 0 it works.
If you look at the documentation from Citrix's Shadow API (actually windows terminal server API's are based on the winframe API's):

BOOL WINAPI WFShadowSessionW(
IN LPWSTR pServerName,
IN ULONG SessionID,
IN ULONG HotKey,
IN ULONG HKModifier,
);

There Servername is passed instead of a handle, so I tried passing ServerName as the second parameter. Nothing happens.

When I look at the Shadow.exe commandline utility, this is the syntax:
SHADOW {sessionname | sessionid} [/SERVER:servername] [/V]

sessionname Identifies the session with name sessionname.
sessionid Identifies the session with ID sessionid.
/SERVER:servername The server containing the session (default is current).
/V Display information about actions being performed.

So perhaps the string could be sessionname? That would make sense, either supply sessionname or sessionid. The other thing is that parameter 1 from WinStationOpenServerW seems to be equal to our parameter 2.

Remko 19. Dez 2006 09:27

Re: WinStationShadow API Function
 
Liste der Anhänge anzeigen (Anzahl: 1)
I did some tests with giving sessionname (eg RDP-Tcp#20) as 2nd parameter, doesn't seem to work. Then I tried with servername, if I specify either computername or IP then remote control works. My tests so far where all on a single server, so this parameter makes it possible to shadow a session on another server. This is probably needed because as Olli determined WinSta actually calls _RpcWinStationShadow. So servername is needed for the RPC part, hServer is probably needed to send the user a message ("domain\user wants to remote control, do you accept?").

So this is it:
function WinStationShadow(hServer: Handle; pServerName: PWideChar; SessionID: ULONG; HotKey: ULong; HKModifier: ULong): Boolean; stdcall;

Tested this with a remote server and it works! For local server you can supply Empty String or Nil that's why it working with ULONG 0.

So could param 3 for WinStationShadowStop also be Servername?
I will also have to test WinStationConnectW as the Unknown ULONG there could also be Servername.

Olli 19. Dez 2006 12:33

Re: WinStationShadow API Function
 
Zitat:

Zitat von Remko
When I pass a PWideChar as second parameter for WinStationShadow nothing happens (no remote control), when I pass ULONG 0 it works.

Pass nil or an empty string. Both are supposed to yield the default behavior you get with ULONG and 0.

Zitat:

Zitat von Remko
So could param 3 for WinStationShadowStop also be Servername?
I will also have to test WinStationConnectW as the Unknown ULONG there could also be Servername.

Yap, test it.

Since RpcWinStationShadow() gets called and it calls MSDN-Library durchsuchenNdrClientCall2(), a badly documented function from RPCRT4.dll, it will be a pain to analyze it in depth. But at least I can tell you the parameters which are passed during that call (the first two and the variable ones). But still it depends on the other side what it does with it, so finding that out is not worth much.

Remko 19. Dez 2006 13:08

Re: WinStationShadow API Function
 
Just tested some more:
When shadowing a session on the same server you must specify hServer and pServername can be empty string or nil.
When shadowing a session on another server you must specify nil for hServer and the remote computername (or IP) for pServername (actually the last method works for both local and remote, so it's easier to use that one always).

I will also test passing pServername to WinStationConnect, WinStationShadowStop is harder because this must be done from a third session (or indeed the shadowed session). Besides what's pServername for WinStationShadowStop, is it the server who's shadowing or the one being shadowed?

Just a though coming up: What if the user who's being shadowed can end the remote control when he or she wants to? That would be a nice feature. It would probably require running some kind of agent in the users session who monitors it's own session state and pops something up when being shadowed. From this popup there could be a "Stop shadowing me" button.

Remko 19. Dez 2006 18:21

Re: WinStationShadow API Function
 
So results so far:
WinStationShadow: 100% working.
WinStationShadowStop: Not needed, just use hotkey to end remote control
WinStationConnectW: Banging my head on this one, I know I've had it working but I just don't get it right now.
Last parameter really seems a PWideChar, passing a value (pServername) to it gives as lasterror Handle is invalid.
When passing nil for the last parameter and passing handle or nil to hServer, the sessionID I want to control and LOGONID_CURRENT gives as LastError: SessionID not found.
Current prototype:
Delphi-Quellcode:
function WinStationConnectW(hServer: HANDLE; ConnectSessionID: ULong; ActiveSession: ULong; pPassword: PWideChar; Unknown: PWideChar): boolean;
NicoDE suggested in this thread: http://www.delphipraxis.net/internal...t.php?p=610883
Type
TFNWinStationConnectW = function(
hServer : THandle;
SessionId : ULONG;
TargetSessionId: ULONG;
pPassword : PWideChar; // use L"" instead of NULL
bWait : BOOLEAN
): BOOLEAN; stdcall;

Olli 20. Dez 2006 02:28

Re: WinStationShadow API Function
 
Well, from my experience I'd trust Nico. He's at least as good of a reverse engineer as I am, if not better! :thumb:

Olli 20. Dez 2006 02:36

Re: WinStationShadow API Function
 
I can verify the first four parameters definitely. I cannot identify the last one from the W2K3 DLL since it uses again some RPC call via an internal function RpcWinStationConnect(). Probably Nico has some older version of the DLL which does not yet use this method and therefore makes it easier to identify this last parameter. Just go for Boolean as he suggests.

Remko 22. Dez 2006 08:57

Re: WinStationShadow API Function
 
I'm still not able to call WinStationConnectW successfully. Because of the time schedule I will result in spawning tscon.exe from commandline and run in the context of an admin account for now.
NicoDE: in case you're reading, can you verify WinStationConnectW? Did you actually get it working?

Will be continued...

Remko 3. Jan 2007 14:04

Re: WinStationShadow API Function
 
Liste der Anhänge anzeigen (Anzahl: 1)
Sometimes people make stupid mistakes, or well at least I do :-).
Somewhere in the experimenting with the parameters I removed stdcall by accident, that's why it didn't work anymore. I just failed to notice it.

So the correct declaration for WinStationConnectW is:
Delphi-Quellcode:
function WinStationConnectW(hServer: Handle; SessionID: ULong; TargetSessionID: ULong; pPassword: PWideChar; bWait:Boolean): Boolean; stdcall;
Where SessionID is the session you want to connect to (can be connected or disconnected) and TargetSessionID is the session to which you want to connect SessionID to (use LOGONID_CURRENT constant for your current session). pPassword cannot be nil, use PWideChar('') instead. If a password is required GetLastError returns 1326 (Logon failure: unknown user name or bad password). The Microsoft tools (eg TSAdmin) work this way, they first try empty password and popup a password dialog and try again if 1326 is returned.

I've attached a new version (v0.3) of WinSta.pas

[edit=SirThornberry]Delphi-Tags gesetzt - Mfg, SirThornberry[/edit]

Olli 7. Jan 2007 06:22

Re: WinStationShadow API Function
 
You can also update the CVS version of JwaWinSta.pas, if you like ;)


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:47 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz