unit uWinStation;
interface
uses SysUtils, Dialogs,
JwaWinType, JwaWinBase, JwaWinNT, JwaNtSecApi, JwaNTStatus, JwaNative,
JwaWinUser, JwaWinError;
function StartInteractiveClientProcess(
const lpszUsername: PAnsiChar;
const lpszDomain: PAnsiChar;
const lpszPassword: PAnsiChar;
const lpCommandLine: PChar): Boolean;
function GetLogonSID(hToken: THandle;
var ppsid: PSID): Boolean;
function AddAceToWindowStation(hwinsta: HWINSTA; psid: PSID): Boolean;
function AddAceToDesktop(hdesktop: HDESK; ps: PSID): Boolean;
function SIDToStr (sid : PSID) :
string;
procedure StrToSid (
const sidName :
string; sid : PSid; sidLen : DWORD);
implementation
const
DESKTOP_ALL = DESKTOP_READOBJECTS
or DESKTOP_CREATEWINDOW
or DESKTOP_CREATEMENU
or DESKTOP_HOOKCONTROL
or
DESKTOP_JOURNALRECORD
or DESKTOP_JOURNALPLAYBACK
or DESKTOP_ENUMERATE
or DESKTOP_WRITEOBJECTS
or
DESKTOP_SWITCHDESKTOP
or STANDARD_RIGHTS_REQUIRED;
WINSTA_ALL = WINSTA_ENUMDESKTOPS
or WINSTA_READATTRIBUTES
or WINSTA_ACCESSCLIPBOARD
or WINSTA_CREATEDESKTOP
or
WINSTA_WRITEATTRIBUTES
or WINSTA_ACCESSGLOBALATOMS
or WINSTA_EXITWINDOWS
or WINSTA_ENUMERATE
or
WINSTA_READSCREEN
or STANDARD_RIGHTS_REQUIRED;
GENERIC_ACCESS = GENERIC_READ
or GENERIC_WRITE
or GENERIC_EXECUTE
or GENERIC_ALL;
HEAP_ZERO_MEMORY = 8;
ACL_REVISION = 2;
ACCESS_ALLOWED_ACE_TYPE = 0;
CONTAINER_INHERIT_ACE = 2;
INHERIT_ONLY_ACE = 8;
OBJECT_INHERIT_ACE = 1;
NO_PROPAGATE_INHERIT_ACE = 4;
SE_GROUP_LOGON_ID = $C0000000;
type
ACL_SIZE_INFORMATION =
record
AceCount: DWORD;
AclBytesInUse: DWORD;
AclBytesFree: DWORD;
end;
ACE_HEADER =
record
AceType: BYTE;
AceFlags: BYTE;
AceSize: WORD;
end;
PACE_HEADER = ^ACE_HEADER;
ACCESS_ALLOWED_ACE =
record
Header: ACE_HEADER;
Mask: ACCESS_MASK;
SidStart: DWORD;
end;
function SIDToStr (sid : PSID) :
string;
var
psia : PSIDIdentifierAuthority;
dwSubAuthorities : DWORD;
dwSidRev : DWORD;
dwCounter : DWORD;
begin
dwSidRev := SID_REVISION;
if IsValidSid (sid)
then
begin
psia := GetSidIdentifierAuthority (sid);
dwSubAuthorities := GetSidSubAuthorityCount (sid)^;
result := Format ('
S-%u-', [dwSidRev]);
if (psia^.Value[0] <> 0)
or (psia^.Value[1] <> 0)
then
result := result + format ('
0x%02x%02x%02x%02x%02x%02x',[
psia^.Value [0],
psia^.Value [1],
psia^.Value [2],
psia^.Value [3],
psia^.Value [4],
psia^.Value [5]])
else
result := result + format ('
%u',
[DWORD (psia^.Value [5]) +
DWORD (psia^.Value [4]
shl 8) +
DWORD (psia^.Value [3]
shl 16) +
DWORD (psia^.Value [2]
shl 24)]);
for dwCounter := 0
to dwSubAuthorities - 1
do
result := result + Format ('
-%u', [GetSidSubAuthority (sid,
dwCounter)^])
end
else
raise Exception.Create ('
Invalid SID');
end;
procedure StrToSid (
const sidName :
string; sid : PSid; sidLen : DWORD);
var
ps : PChar;
pn : PChar;
p : PChar;
pa : PChar;
valueStr :
string;
psia : PSIDIdentifierAuthority;
i : DWORD;
d : DWORD;
authorityCount : DWORD;
begin
(*
typedef struct _SID {
BYTE Revision;
BYTE SubAuthorityCount;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority;
#ifdef MIDL_PASS
[size_is(SubAuthorityCount)] DWORD SubAuthority[*];
#else // MIDL_PASS
DWORD SubAuthority[ANYSIZE_ARRAY];
*)
if not ((Length (sidName) > 3)
and (sidName [1] = '
S')
and (sidName [2] =
'
-'))
then
raise Exception.Create ('
Bad SID');
if sidLen < sizeof (_SID_IDENTIFIER_AUTHORITY) + 2
then
raise Exception.Create ('
Bad SID');
ps := PChar (sid);
pn := PChar (sidName);
Inc (pn, 2);
p := StrScan (pn, '
-');
if not Assigned (p)
then
raise Exception.Create ('
Bad SID');
p^ := #0;
ps^ := char (StrToInt (pn));
// Revision
Inc (ps);
pa := ps;
// Save authority count position
Inc (ps);
pn := p + 1;
p := StrScan (pn, '
-');
if not Assigned (p)
then
raise Exception.Create ('
Bad SID');
p^ := #0;
valueStr := pn;
if Length (valueStr) < 1
then
raise Exception.Create ('
Bad SID');
psia := PSIDIdentifierAuthority (ps);
Inc (ps, sizeof (_SID_IDENTIFIER_AUTHORITY));
Dec (sidLen, 2 + sizeof (_SID_IDENTIFIER_AUTHORITY));
if valueStr [1] = '
x'
then
begin
if Length (valueStr) <> 14
then
raise Exception.Create ('
Bad SID');
psia^.value [0] := StrToInt ('
$' + Copy (valueStr, 3, 2));
psia^.value [1] := StrToInt ('
$' + Copy (valueStr, 5, 2));
psia^.value [2] := StrToInt ('
$' + Copy (valueStr, 7, 2));
psia^.value [3] := StrToInt ('
$' + Copy (valueStr, 9, 2));
psia^.value [4] := StrToInt ('
$' + Copy (valueStr, 11, 2));
psia^.value [5] := StrToInt ('
$' + Copy (valueStr, 13, 2))
end
else
begin
psia^.value [0] := 0;
psia^.value [1] := 0;
i := StrToInt (valueStr);
d := i
shl 24;
psia^.value [2] := d
and $ff;
d := i
shl 16;
psia^.value [3] := d
and $ff;
d := i
shl 8;
psia^.value [4] := d
and $ff;
psia^.value [5] := i
and $ff;
end;
pn := p + 1;
authorityCount := 0;
while lstrlen (pn) > 0
do
begin
p := StrScan (pn, '
-');
if Assigned (p)
then
begin
p^ := #0;
i := StrToInt (pn);
pn := p + 1
end
else
begin
i := StrToInt (pn);
pn := pn + lstrlen (pn)
end;
if sidLen < sizeof (DWORD)
then
raise Exception.Create ('
Bad SID');
PDWORD (ps)^ := i;
Inc (ps, sizeof (DWORD));
Dec (sidLen, sizeof (DWORD));
Inc (authorityCount);
end;
pa^ := char (authorityCount);
if not IsValidSID (sid)
then
raise Exception.Create ('
Bad SID');
end;
function AddAceToWindowStation(hwinsta: HWINSTA; psid: PSID): Boolean;
var
si: SECURITY_INFORMATION;
psd, psdNew: PSECURITY_DESCRIPTOR;
dwSidSize, dwSdSizeNeeded, dwNewAclSize: DWORD;
bDaclPresent, bDaclExist: LongBool;
pdacl, pNewAcl: PACL;
aclSizeInfo: ACL_SIZE_INFORMATION;
i: integer;
pTempAce: PACE_HEADER;
pace: ^ACCESS_ALLOWED_ACE;
begin
Result := False;
si := DACL_SECURITY_INFORMATION;
pace :=
nil;
psd :=
nil;
dwSidSize := 0;
pNewAcl :=
nil;
psdNew :=
nil;
// Obtain the DACL for the window station.
try
if not GetUserObjectSecurity(hwinsta, si, psd, dwSidSize, dwSdSizeNeeded)
then begin
if GetLastError = ERROR_INSUFFICIENT_BUFFER
then begin
psd := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psd =
nil then
Exit;
psdNew := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psdNew =
nil then
Exit;
dwSidSize := dwSdSizeNeeded;
if not GetUserObjectSecurity(hwinsta, si, psd, dwSidSize, dwSdSizeNeeded)
then
Exit;
end
else begin
Exit;
end;
end;
// Create a new DACL.
if not InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION)
then
Exit;
// Get the DACL from the security descriptor.
if not GetSecurityDescriptorDacl(psd, bDaclPresent, pdacl, bDaclExist)
then
Exit;
// Initialize the ACL.
ZeroMemory(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION));
aclSizeInfo.AclBytesInUse := SizeOf(
ACL);
// Call only if the DACL is not NULL.
if pdacl <>
nil then begin
// get the file ACL size info
if not GetAclInformation(pdacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation)
then
Exit;
end;
// Compute the size of the new ACL.
dwNewAclSize := aclSizeInfo.AclBytesInUse + (2 * SizeOf(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid(psid)) - (2 * SizeOf(DWORD));
// Allocate memory for the new ACL.
pNewAcl := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwNewAclSize);
if pNewAcl =
nil then
Exit;
// Initialize the new DACL.
if not InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION)
then
Exit;
// If DACL is present, copy it to a new DACL.
if bDaclPresent
then begin
// Copy the ACEs to the new ACL.
if aclSizeInfo.AceCount > 0
then begin
for i := 0
to aclSizeInfo.AceCount - 1
do begin
// Get an ACE.
if not GetAce(pdacl, i, Pointer(pTempAce))
then
Exit;
// Add the ACE to the new ACL.
if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pTempAce, pTempAce.AceSize)
then
Exit;
end;
end;
end;
// Add the first ACE to the window station.
pace := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - SizeOf(DWORD));
if pace =
nil then
Exit;
pace.Header.AceType := ACCESS_ALLOWED_ACE_TYPE;
pace.Header.AceFlags := CONTAINER_INHERIT_ACE
or INHERIT_ONLY_ACE
or OBJECT_INHERIT_ACE;
pace.Header.AceSize := SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(psid) - SizeOf(DWORD);
pace.Mask := GENERIC_ACCESS;
if not CopySid(GetLengthSid(psid), @pace.SidStart, psid)
then
Exit;
if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pace, pace.Header.AceSize)
then
Exit;
// Add the second ACE to the window station.
pace.Header.AceFlags := NO_PROPAGATE_INHERIT_ACE;
pace.Mask := WINSTA_ALL;
if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pace, pace.Header.AceSize)
then
Exit;
// Set a new DACL for the security descriptor.
if not SetSecurityDescriptorDacl(psdNew, True, pNewAcl, False)
then
Exit;
// Set the new security descriptor for the window station.
if not SetUserObjectSecurity(hwinsta, si, psdNew)
then
Exit;
// Indicate success.
Result := True;
finally
// Free the allocated buffers.
if pace <>
nil then
HeapFree(GetProcessHeap, 0, pace);
if pNewAcl <>
nil then
HeapFree(GetProcessHeap, 0, pNewAcl);
if psd <>
nil then
HeapFree(GetProcessHeap, 0, psd);
if psdNew <>
nil then
HeapFree(GetProcessHeap, 0, psdNew);
end;
end;
function GetLogonSID(hToken: THandle;
var ppsid: PSID): Boolean;
var dwLength: DWORD;
ptg: ^TOKEN_GROUPS;
i: integer;
begin
Result := False;
dwLength := 0;
ptg :=
nil;
try
// Verify the parameter passed in is not NULL.
// if ppsid = nil then
// Exit;
// Get required buffer size and allocate the TOKEN_GROUPS buffer.
if not GetTokenInformation(hToken, TokenGroups, ptg, 0, dwLength)
then
begin
if GetLastError <> ERROR_INSUFFICIENT_BUFFER
then
begin
ShowMessage('
GetTokenInformation failed');
Exit;
end;
ptg := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
if ptg =
nil then
begin
Exit;
end;
// Get the token group information from the access token.
if not GetTokenInformation(hToken, TokenGroups, ptg, dwLength, dwLength)
then
begin
Exit;
end;
// Loop through the groups to find the logon SID.
for i := 0
to ptg.GroupCount-1
do
begin
if ptg.Groups[i].Attributes
and SE_GROUP_LOGON_ID = SE_GROUP_LOGON_ID
then
begin
// Found the logon SID; make a copy of it.
dwLength := GetLengthSid(ptg.Groups[i].Sid);
ppsid := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwLength);
if ppsid =
nil then
begin
Exit;
end;
if not CopySid(dwLength, ppsid, ptg.Groups[i].Sid)
then
begin
raise exception.Create(Format('
CopySid: %s', [SysErrorMessage(GetLastError)]));
HeapFree(GetProcessHeap, 0, ppsid);
Exit;
end;
Break;
end;
end;
Result := True;
end;
finally
// Free the buffer for the token groups.
if ptg <>
nil then
begin
HeapFree(GetProcessHeap, 0, ptg);
end;
end;
end;
function AddAceToDesktop(hdesktop: HDESK; ps: PSID): Boolean;
var
aclSizeInfo: ACL_SIZE_INFORMATION;
bDaclExist, bDaclPresent: LongBool;
dwNewAclSize, dwSidSize, dwSdSizeNeeded: DWORD;
pdacl, pNewAcl: PACL;
psd, psdNew: PSECURITY_DESCRIPTOR;
pTempAce: PACE_HEADER;
si: SECURITY_INFORMATION;
i: integer;
begin
Result := False;
psd :=
nil;
psdNew :=
nil;
pNewAcl :=
nil;
si := DACL_SECURITY_INFORMATION;
dwSidSize := 0;
try
// Obtain the security descriptor for the desktop object.
if not GetUserObjectSecurity(hdesktop, si, psd, dwSidSize, dwSdSizeNeeded)
then begin
if GetLastError = ERROR_INSUFFICIENT_BUFFER
then begin
psd := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psd =
nil then
Exit;
psdNew := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwSdSizeNeeded);
if psdNew =
nil then
Exit;
dwSidSize := dwSdSizeNeeded;
if not GetUserObjectSecurity(hdesktop, si, psd, dwSidSize, dwSdSizeNeeded)
then
Exit;
end
else begin
Exit;
end;
end;
// Create a new security descriptor.
if not InitializeSecurityDescriptor(psdNew, SECURITY_DESCRIPTOR_REVISION)
then
Exit;
// Obtain the DACL from the security descriptor.
if not GetSecurityDescriptorDacl(psd, bDaclPresent, pdacl, bDaclExist)
then
Exit;
// Initialize.
ZeroMemory(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION));
aclSizeInfo.AclBytesInUse := SizeOf(
ACL);
// Call only if NULL DACL.
if pdacl <>
nil then begin
// Determine the size of the ACL information.
if not GetAclInformation(pdacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), AclSizeInformation)
then
Exit;
end;
// Compute the size of the new ACL.
dwNewAclSize := aclSizeInfo.AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid(ps) - SizeOf(DWORD);
// Allocate buffer for the new ACL.
pNewAcl := HeapAlloc(GetProcessHeap, HEAP_ZERO_MEMORY, dwNewAclSize);
if pNewAcl =
nil then
Exit;
// Initialize the new ACL.
if not InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION)
then
Exit;
// If DACL is present, copy it to a new DACL.
if bDaclPresent
then begin
// Copy the ACEs to the new ACL.
if aclSizeInfo.AceCount > 0
then begin
for i := 0
to aclSizeInfo.AceCount - 1
do begin
// Get an ACE.
if not GetAce(pdacl, i, Pointer(pTempAce))
then
Exit;
// Add the ACE to the new ACL.
if not AddAce(pNewAcl, ACL_REVISION, MAXDWORD, pTempAce, pTempAce.AceSize)
then
Exit;
end;
end;
end;
// Add ACE to the DACL.
if not AddAccessAllowedAce(pNewAcl, ACL_REVISION, DESKTOP_ALL, ps)
then
Exit;
// Set new DACL to the new security descriptor.
if not SetSecurityDescriptorDacl(psdNew, True, pNewAcl, False)
then
Exit;
// Set the new security descriptor for the desktop object.
if not SetUserObjectSecurity(hdesktop, si, psdNew)
then
Exit;
// Indicate success.
Result := True;
finally
// Free buffers.
if pNewAcl <>
nil then
HeapFree(GetProcessHeap, 0, pNewAcl);
if psd <>
nil then
HeapFree(GetProcessHeap(), 0, psd);
if psdNew <>
nil then
HeapFree(GetProcessHeap(), 0, psdNew);
end;
end;
function StartInteractiveClientProcess(
const lpszUsername: PAnsiChar;
const lpszDomain: PAnsiChar;
const lpszPassword: PAnsiChar;
const lpCommandLine: PChar): Boolean;
var hToken : THandle;
hdesktop : HDESK;
hwinst : HWINSTA;
hwinstSave: HWINSTA;
pi : PROCESS_INFORMATION;
pS : PSID;
si : STARTUPINFO;
begin
Result := False;
hdesktop := 0;
hwinst := 0;
hwinstSave := 0;
pS :=
nil;
// try
// Log the client on to the local computer.
// if not LogonUser(lpszUsername, lpszDomain, lpszPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, hToken) then
if not LogonUser(lpszUsername, lpszDomain, lpszPassword, LOGON32_LOGON_NEW_CREDENTIALS, LOGON32_PROVIDER_WINNT50, hToken)
then
begin
ShowMessageFmt('
Logonuser failed: %s', [SysErrorMessage(GetLastError)]);
Result := False;
// raise exception.create(Format('LogonUser: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
OutputDebugString(PChar(Format('
LogonUser: %s', [SysErrorMessage(GetLastError)])));
// Save a handle to the caller's current window station.
hwinstSave := GetProcessWindowStation;
if hwinstSave = 0
then
begin
raise exception.create(Format('
GetProcessWindowStation: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
OutputDebugString(PChar(Format('
GetProcessWindowStation: %s', [SysErrorMessage(GetLastError)])));
//\Sessions\2\Windows\WindowStations\WinSta0\Default
// Get a handle to the interactive window station.
hwinst := OpenWindowStation('
WinSta0', False, READ_CONTROL
or WRITE_DAC);
if hwinst = 0
then
begin
raise exception.create(Format('
OpenWindowStation: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
OutputDebugString(PChar(Format('
OpenWindowStation: %s', [SysErrorMessage(GetLastError)])));
// To get the correct default desktop, set the caller's
// window station to the interactive window station.
if not SetProcessWindowStation(hwinst)
then
begin
raise exception.create(Format('
SetProcessWindowStation(hwinst): %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
OutputDebugString(PChar(Format('
SetProcessWindowStation(hwinst): %s', [SysErrorMessage(GetLastError)])));
// Get a handle to the interactive desktop.
hdesktop := OpenDesktop('
default', 0, False, READ_CONTROL
or WRITE_DAC
or DESKTOP_WRITEOBJECTS
or DESKTOP_READOBJECTS);
if hdesktop = 0
then
begin
raise exception.create(Format('
OpenDesktop: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
OutputDebugString(PChar(Format('
OpenDesktop: %s', [SysErrorMessage(GetLastError)])));
// Restore the caller's window station.
if not SetProcessWindowStation(hwinstSave)
then
begin
raise exception.create(Format('
SetProcessWindowStation(hwinstSave): %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
// Get the SID for the client's logon session.
if not GetLogonSID(hToken, pS)
then
begin
raise exception.create(Format('
GetLogonSID: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
// Allow logon SID full access to interactive window station.
if not AddAceToWindowStation(hwinst, pS)
then
begin
raise exception.create(Format('
AddAceToWindowStation: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
// Allow logon SID full access to interactive desktop.
if not AddAceToDesktop(hdesktop, pS)
then
begin
raise exception.create(Format('
AddAceToDesktop: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
// Impersonate client to ensure access to executable file.
if not ImpersonateLoggedOnUser(hToken)
then
begin
raise exception.create(Format('
ImpersonateLoggedOnUser: %s', [SysErrorMessage(GetLastError)]));
Exit;
end;
// Initialize the STARTUPINFO structure.
// Specify that the process runs in the interactive desktop.
ZeroMemory(@si, SizeOf(STARTUPINFO));
si.cb := SizeOf(STARTUPINFO);
si.lpDesktop := PChar('
Sessions\3\Windows\WindowStations\WinSta0\Default');
// Launch the process in the client's logon session.
Result := CreateProcessAsUser(hToken,
nil, lpCommandLine,
nil,
nil, False,
// handles are not inheritable
NORMAL_PRIORITY_CLASS
or CREATE_NEW_CONSOLE,
nil,
nil, si, pi);
// End impersonation of client.
RevertToSelf();
if Result
and (pi.hProcess <> INVALID_HANDLE_VALUE)
then
begin
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
end;
if pi.hThread <> INVALID_HANDLE_VALUE
then
begin
CloseHandle(pi.hThread);
end;
Result := True;
// finally
if hwinstSave <> 0
then
begin
SetProcessWindowStation(hwinstSave);
end;
// Free the buffer for the logon SID.
if pS <>
nil then
begin
HeapFree(GetProcessHeap, 0, pS);
end;
// Close the handles to the interactive window station and desktop.
if hwinst <> 0
then
begin
CloseWindowStation(hwinst);
end;
if hdesktop <> 0
then
begin
CloseDesktop(hdesktop);
end;
// Close the handle to the client's access token.
if hToken <> INVALID_HANDLE_VALUE
then
begin
CloseHandle(hToken);
end;
end;
//end;
end.