C++ conversion of interactive process

Windows specific forum
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

C++ conversion of interactive process

Post by tatanas »

Hi,

I would like to do exactly this : https://learn.microsoft.com/en-us/previ ... 8(v=vs.85)

I tried to convert this C++ code in Purebasic but my knowledge is insufficient to make it work.

Could someone help me ?

Thank you for your time.

EDIT : I fixed some errors. The last error is coming from the CreateProcessAsUser_ (1314 : A required privilege is not held by the client)
Here is what I did :

Code: Select all

Procedure Hiword(a.l)
  ProcedureReturn (a>>16 & $ffff)
EndProcedure

Procedure LOWORD(a.l)
  ProcedureReturn (a & $ffff)
EndProcedure

Procedure WriteToLog(entry.s)
	Debug entry
; 	Protected hFile.l
; 	hFile = OpenFile(#PB_Any, "", #PB_File_SharedRead | #PB_File_SharedWrite)
; 	If hFile = #Null
; 		ProcedureReturn #False
; 	EndIf
; 	FileSeek(hFile, Lof(hFile))
; 	WriteStringN(hFile, entry)
; 	CloseFile(hFile)
; 	ProcedureReturn #True
EndProcedure

; https://github.com/poweradminllc/PAExec/blob/master/InteractiveSession.cpp
; https://www.installsetupconfig.com/win32programming/windowstationsdesktops13_5.html
; https://learn.microsoft.com/en-us/previous-versions/aa379608(v=vs.85)

Declare AddAceToWindowStation(hwinsta, *psid)
Declare AddAceToDesktop(hdesk, psid)
; Declare RemoveAceFromWindowStation(hwinsta, psid)
; Declare RemoveAceFromDesktop(hdesk, psid)
Declare GetLogonSID(hToken, *ppsid)
Declare FreeLogonSID(*ppsid) 


Procedure AccessWinStation(hToken)

	Protected hdesk = #Null
	Protected hwinsta = #Null
	Protected pSid = #Null
	Protected hwinstaSave = #Null
	Protected err
	
	hwinstaSave = GetProcessWindowStation_()
	; Save a handle To the caller's current window station.
	If hwinstaSave = #Null
		err = GetLastError_()
		WriteToLog("Failed to get GetProcessWindowStation : " + err)
		Goto Cleanup_AccessWinStation
	EndIf
	
	; Get a handle To the interactive window station.
	hwinsta = OpenWindowStation_(@"winsta0", #False, #READ_CONTROL | #WRITE_DAC)
	
	If hwinsta = #Null
		err = GetLastError_()
		WriteToLog("Failed to open winsta0 : " + err)
		Goto Cleanup_AccessWinStation
	EndIf
Debug "1"

	; To get the correct Default desktop, set the caller's window station To the interactive window station.
	If Not SetProcessWindowStation_(hwinsta)
		err = GetLastError_()
		WriteToLog("Failed to SetProcessWindowStation : " + err)
		Goto Cleanup_AccessWinStation
	EndIf
	
	; Get a handle To the interactive desktop.
	hdesk = OpenDesktop_(@"default", 0, #False, #READ_CONTROL | #WRITE_DAC | #DESKTOP_WRITEOBJECTS | #DESKTOP_READOBJECTS)
	
	err = GetLastError_()
	
	; Restore the caller's window station.
	If Not SetProcessWindowStation_(hwinstaSave)
		Goto Cleanup_AccessWinStation
	EndIf
	
	If hdesk = #Null
		WriteToLog("Failed to get Default desktop : " + err)
		Goto Cleanup_AccessWinStation
	EndIf
Debug "2"	
	
	; Get the SID For the client's logon session.
	If Not GetLogonSID(hToken, @pSid)
		WriteToLog("Failed to get login SID")
		Goto Cleanup_AccessWinStation
	EndIf
Debug "3"	
	
	; Allow logon SID full access To interactive window station.
	If Not AddAceToWindowStation(hwinsta, @pSid)
		err = GetLastError_()
		WriteToLog("Failed to add ACE to WinStation : " + err)
		CloseWindowStation_(hwinsta)
		hwinsta = #Null; so it's not removed and cleaned up later
		Goto Cleanup_AccessWinStation
	EndIf
Debug "4"

	; Allow logon SID full access To interactive desktop.
	If Not AddAceToDesktop(hdesk, @pSid)
		err = GetLastError_()
		WriteToLog("Failed to add ACE to Desktop : " + err)
		CloseDesktop_(hdesk)
		hdesk = #Null
		Goto Cleanup_AccessWinStation
	EndIf
Debug "5"


	Protected si.STARTUPINFO
	ZeroMemory_(@si, SizeOf(STARTUPINFO))
		si\cb = SizeOf(STARTUPINFO)
		si\lpDesktop = @"WinSta0\Default"

	Protected pi.PROCESS_INFORMATION
	ZeroMemory_(@pi, SizeOf(PROCESS_INFORMATION))
	#CREATE_UNICODE_ENVIRONMENT = $400
	Protected dwCreationFlags = #CREATE_SUSPENDED | #CREATE_NEW_CONSOLE | #CREATE_UNICODE_ENVIRONMENT

	ImpersonateLoggedOnUser_(hToken)

	Protected lpCommandLine = #Null
	If CreateProcessAsUser_(hToken, #Null, @"c:\windows\system32\notepad.exe", #Null, #Null, #False, dwCreationFlags, #Null, #Null, @si, @pi)
		ResumeThread_(pi\hThread)
		CloseHandle_(pi\hThread)
	Else
		error = GetLastError_()
		WriteToLog("CreateProcessAsUser_ : " + error)
	EndIf

	RevertToSelf_()

	
Cleanup_AccessWinStation: 
	If hwinstaSave : SetProcessWindowStation_(hwinstaSave) : EndIf

	; Free the buffer for the logon SID
	If pSid : FreeLogonSID(@pSid) : EndIf

	; Close the handles to the interactive window station and desktop.
   If hwinsta : CloseWindowStation_(hwinsta) : EndIf
   If hdesk : CloseDesktop_(hdesk) : EndIf

	; Close the handle To the client's access token.
   If hToken <> #INVALID_HANDLE_VALUE : CloseHandle_(hToken) : EndIf

EndProcedure


Procedure FreeLogonSID(*ppsid) 
	HeapFree_(GetProcessHeap_(), 0, *ppsid)
EndProcedure


Procedure GetLogonSID(hToken, *ppsid) 

	Structure TOKEN_USER
		User.SID_AND_ATTRIBUTES
	EndStructure

	Protected bSuccess = #False
	Protected dwIndex
	Protected dwLength = 0
	Protected ptg.TOKEN_GROUPS
	Protected *pTU.TOKEN_USER
	Protected err

	; Verify the parameter passed in is Not NULL.
	If *ppsid = #Null
		Goto Cleanup_GetLogonSID
	EndIf

	SetLastError_(0)
	If GetTokenInformation_(hToken, #TokenUser, 0, 0, @dwLength) = #False And GetLastError_() = #ERROR_INSUFFICIENT_BUFFER And dwLength
		
		*pTU = AllocateMemory(dwLength)
		If *pTU	
			If GetTokenInformation_(hToken, #TokenUser, *pTU, dwLength, @dwLength)
			
				If Not CopySid_(dwLength, *ppsid, *pTU\User\Sid)
					HeapFree_(GetProcessHeap_(), 0, *ppsid)
					Goto Cleanup_GetLogonSID
				EndIf
Debug "2.1"
				bSuccess = #True
				Goto Cleanup_GetLogonSID

			Else
				err = GetLastError_()
				WriteToLog("Failed to get login token information 2: " + err)
				Goto Cleanup_GetLogonSID
			EndIf
			
			FreeMemory(*pTU)
		EndIf
	Else
		err = GetLastError_()
		WriteToLog("Failed to get login token information 1: " + err)
		Goto Cleanup_GetLogonSID	
	EndIf

Debug "2.2"

	; fall through And make alternate attempt

	; Get required buffer size And allocate the TOKEN_GROUPS buffer.
	If Not GetTokenInformation_(hToken, #TokenGroups, ptg, 0, @dwLength)
		If GetLastError_() <> #ERROR_INSUFFICIENT_BUFFER
			err = GetLastError_()
			WriteToLog("Failed to get login token information[2] : " + err)
			Goto Cleanup_GetLogonSID
		EndIf

; 		ptg = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwLength)
; 		If ptg = #Null
; 			Goto Cleanup_GetLogonSID
; 		EndIf
	EndIf
Debug "2.3"

	; Get the token group information from the access token.
	If Not GetTokenInformation_(hToken, #TokenGroups, ptg, dwLength, @dwLength)
		Goto Cleanup_GetLogonSID
	EndIf

	; Loop through the groups To find the logon SID.
	For dwIndex = 0 To ptg\GroupCount - 1
		If ptg\Groups[dwIndex]\Attributes & #SE_GROUP_LOGON_ID = #SE_GROUP_LOGON_ID
			; Found the logon SID; make a copy of it.
			dwLength = GetLengthSid_(ptg\Groups[dwIndex]\Sid)
			*ppsid = HeapAlloc_(GetProcessHeap_(),	#HEAP_ZERO_MEMORY, dwLength)
			If *ppsid = #Null
				Goto Cleanup_GetLogonSID
			EndIf
			If Not CopySid_(dwLength, *ppsid, ptg\Groups[dwIndex]\Sid)
				HeapFree_(GetProcessHeap_(), 0, *ppsid)
				Goto Cleanup_GetLogonSID
			EndIf
			bSuccess = #True
			Break
		EndIf
	Next


Cleanup_GetLogonSID: 
	If ptg <> #Null : HeapFree_(GetProcessHeap_(), 0, ptg) : EndIf
; 	ptg = #Null
; 	If pTU <> #Null : HeapFree_(GetProcessHeap_(), 0, *pTU) : EndIf
	FreeMemory(*pTU)
; 	pTU = #Null

	ProcedureReturn bSuccess

EndProcedure


Procedure AddAceToWindowStation(hwinsta, psid)

; 	Protected *pace = #Null
	Protected aclSizeInfo.ACL_SIZE_INFORMATION
	Protected bDaclExist
	Protected bDaclPresent
	Protected bSuccess = #False
	Protected dwNewAclSize
	Protected dwSidSize = 0
	Protected dwSdSizeNeeded
	Protected pacl
	Protected pNewAcl = #Null
	Protected psd = #Null
	Protected psdNew = #Null
	Protected pTempAce
	Protected *aceHeader.ACE_HEADER
	#DACL_SECURITY_INFORMATION = 4
	Protected si = #DACL_SECURITY_INFORMATION
	Protected i

	; Obtain the DACL For the window station.
	If Not GetUserObjectSecurity_(hwinsta, @si, psd, dwSidSize, @dwSdSizeNeeded)

		If GetLastError_() = #ERROR_INSUFFICIENT_BUFFER

			psd = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
			If psd = #Null : ProcedureReturn 0 : EndIf
					
			psdNew = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
			If psdNew = #Null : ProcedureReturn 0 : EndIf

			dwSidSize = dwSdSizeNeeded
			If Not GetUserObjectSecurity_(hwinsta, @si, psd, dwSidSize, @dwSdSizeNeeded)
				ProcedureReturn 0
			EndIf

		Else
			ProcedureReturn 0
		EndIf

	EndIf
Debug "3.1"

	; Create a new DACL.
	If Not InitializeSecurityDescriptor_(psdNew, #SECURITY_DESCRIPTOR_REVISION)
		ProcedureReturn 0
	EndIf

	; Get the DACL from the security descriptor.
	If Not GetSecurityDescriptorDacl_(psd, @bDaclPresent, @pacl, @bDaclExist)
		ProcedureReturn 0
	EndIf
Debug "3.2"

	; Initialize the ACL.
	ZeroMemory_(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION))
	aclSizeInfo\AclBytesInUse = SizeOf(ACL) ; ACL ???

	; Call only If the DACL is Not NULL.
	If pacl <> #Null
		; get the file ACL size info
		If Not GetAclInformation_(pacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), #AclSizeInformation)
			ProcedureReturn 0
		EndIf
	EndIf
Debug "3.3"

	; Compute the size of the new ACL.
	dwNewAclSize = aclSizeInfo\AclBytesInUse + (2 * SizeOf(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid_(psid)) - (2 * SizeOf(LONG))

	; Allocate memory For the new ACL.
	pNewAcl = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwNewAclSize)
	If pNewAcl = #Null : ProcedureReturn 0 : EndIf

	;Initialize the new DACL.
	If Not InitializeAcl_(pNewAcl, dwNewAclSize, #ACL_REVISION)
		ProcedureReturn 0
	EndIf

	; If DACL is present, copy it To a new DACL.
	If bDaclPresent
		; Copy the ACEs To the new ACL.
		If aclSizeInfo\AceCount

			For i = 0 To aclSizeInfo\AceCount - 1
				; Get an ACE.
				If Not GetAce_(pacl, i, @pTempAce)
					ProcedureReturn 0
				EndIf

				*aceHeader = pTempAce

				; Add the ACE To the new ACL.
				If Not AddAce_(pNewAcl, #ACL_REVISION, #MAXDWORD, pTempAce, *aceHeader\AceSize) ; ?????
					ProcedureReturn 0
				EndIf

			Next
		EndIf
	EndIf
Debug "3.4"

	;Add the first ACE To the window station.
	Protected *pace.ACCESS_ALLOWED_ACE ;= AllocateStructure(ACCESS_ALLOWED_ACE)

	*pace = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(psid) - SizeOf(LONG)) ; ?????
	If *pace = #Null
		ProcedureReturn 0
	EndIf

	#GENERIC_ACCESS = #GENERIC_READ | #GENERIC_WRITE | #GENERIC_EXECUTE | #GENERIC_ALL
	#WINSTA_ALL = #WINSTA_ENUMDESKTOPS | #WINSTA_READATTRIBUTES | #WINSTA_ACCESSCLIPBOARD | #WINSTA_CREATEDESKTOP | #WINSTA_WRITEATTRIBUTES | #WINSTA_ACCESSGLOBALATOMS | #WINSTA_EXITWINDOWS | #WINSTA_ENUMERATE | #WINSTA_READSCREEN | #STANDARD_RIGHTS_REQUIRED

	*pace\Header\AceType  = #ACCESS_ALLOWED_ACE_TYPE
	*pace\Header\AceFlags = #CONTAINER_INHERIT_ACE | #INHERIT_ONLY_ACE | #OBJECT_INHERIT_ACE
	*pace\Header\AceSize  = LOWORD(SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(psid) - SizeOf(LONG))
	*pace\Mask            = #GENERIC_ACCESS

	If Not CopySid_(GetLengthSid_(psid), @*pace\SidStart, psid) ; ????????????
		ProcedureReturn 0
	EndIf
Debug "3.5"

	If Not AddAce_(pNewAcl, #ACL_REVISION, #MAXDWORD, *pace, *pace\Header\AceSize) ; ?????
		ProcedureReturn 0
	EndIf
Debug "3.6"

	; 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) ; ????
		ProcedureReturn 0
	EndIf
Debug "3.7"

	;Set a new DACL For the security descriptor.
	If Not SetSecurityDescriptorDacl_(psdNew, #True, pNewAcl, #False)
		ProcedureReturn 0
	EndIf
Debug "3.8"

	; Set the new security descriptor For the window station.
	If Not SetUserObjectSecurity_(hwinsta, @si, psdNew)
		ProcedureReturn 0
	EndIf

	; Indicate success.
	bSuccess = #True
Debug "3.9"

	; Free the allocated buffers.
	If *pace <> #Null : HeapFree_(GetProcessHeap_(), 0, *pace) : EndIf
	If pNewAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, pNewAcl) : EndIf
	If psd <> #Null : HeapFree_(GetProcessHeap_(), 0, psd) : EndIf
	If psdNew <> #Null : HeapFree_(GetProcessHeap_(), 0, psdNew) : EndIf

	ProcedureReturn bSuccess

EndProcedure


Procedure AddAceToDesktop(hdesk, psid)

	Protected aclSizeInfo.ACL_SIZE_INFORMATION
	Protected bDaclExist
	Protected bDaclPresent
	Protected bSuccess = #False
	Protected dwNewAclSize
	Protected dwSidSize = 0
	Protected dwSdSizeNeeded
	Protected pacl
	Protected pNewAcl = #Null
	Protected psd = #Null
	Protected psdNew = #Null
	Protected pTempAce;.ACE_HEADER
	Protected *aceHeader.ACE_HEADER
	#DACL_SECURITY_INFORMATION = 4
	Protected si = #DACL_SECURITY_INFORMATION
	Protected i


	; Obtain the security descriptor for the desktop object.
	If Not GetUserObjectSecurity_(hdesk, @si, psd, dwSidSize, @dwSdSizeNeeded)

		If GetLastError_() = #ERROR_INSUFFICIENT_BUFFER

			psd = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
			If psd = #Null : ProcedureReturn 0 : EndIf
					
			psdNew = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
			If psdNew = #Null : ProcedureReturn 0 : EndIf

			dwSidSize = dwSdSizeNeeded
			If Not GetUserObjectSecurity_(hdesk, @si, psd, dwSidSize, @dwSdSizeNeeded)
				ProcedureReturn 0
			EndIf

		Else
			ProcedureReturn 0
		EndIf

	EndIf
Debug "4.1"

	; Create a new security descriptor
	If Not InitializeSecurityDescriptor_(psdNew, #SECURITY_DESCRIPTOR_REVISION)
		ProcedureReturn 0
	EndIf
Debug "4.2"

	; Obtain the DACL from the security descriptor.
	If Not GetSecurityDescriptorDacl_(psd, @bDaclPresent, @pacl, @bDaclExist)
		ProcedureReturn 0
	EndIf
Debug "4.3"

	; Initialize the ACL.
	ZeroMemory_(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION))
	aclSizeInfo\AclBytesInUse = SizeOf(ACL)

	; Call only If the DACL is Not NULL.
	If pacl <> #Null
		; get the file ACL size info
		If Not GetAclInformation_(pacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), #AclSizeInformation) 
			ProcedureReturn 0
		EndIf
	EndIf
Debug "4.4"

	; Compute the size of the new ACL.
	dwNewAclSize = aclSizeInfo\AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(psid) - SizeOf(LONG)

	; Allocate memory For the new ACL.
	pNewAcl = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwNewAclSize)
	If pNewAcl = #Null : ProcedureReturn 0 : EndIf

	;Initialize the new DACL.
	If Not InitializeAcl_(pNewAcl, dwNewAclSize, #ACL_REVISION)
		ProcedureReturn 0
	EndIf
Debug "4.5"

	; If DACL is present, copy it To a new DACL.
	If bDaclPresent
		; Copy the ACEs To the new ACL.
		If aclSizeInfo\AceCount
			For i = 0 To aclSizeInfo\AceCount - 1
				; Get an ACE.
				If Not GetAce_(pacl, i, @pTempAce)
					ProcedureReturn 0
				EndIf

				*aceHeader = pTempAce

				; Add the ACE To the new ACL.
				If Not AddAce_(pNewAcl, #ACL_REVISION, #MAXDWORD, pTempAce, *aceHeader\AceSize) ; ?????
					ProcedureReturn 0
				EndIf
			Next
		EndIf
	EndIf
Debug "4.6"

	#DESKTOP_ALL = #DESKTOP_READOBJECTS | #DESKTOP_CREATEWINDOW | #DESKTOP_CREATEMENU | #DESKTOP_HOOKCONTROL | #DESKTOP_JOURNALRECORD | #DESKTOP_JOURNALPLAYBACK | #DESKTOP_ENUMERATE | #DESKTOP_WRITEOBJECTS | #DESKTOP_SWITCHDESKTOP | #STANDARD_RIGHTS_REQUIRED

	; Add ACE To the DACL.
	If Not AddAccessAllowedAce_(pNewAcl, #ACL_REVISION, #DESKTOP_ALL, psid)
		ProcedureReturn 0
	EndIf
Debug "4.7"

	; Set new DACL To the new security descriptor.
	If Not SetSecurityDescriptorDacl_(psdNew, #True, pNewAcl, #False)
		ProcedureReturn 0
	EndIf
Debug "4.8"

	; Set the new security descriptor For the desktop object.
	If Not SetUserObjectSecurity_(hdesk, @si, psdNew)
		ProcedureReturn 0
	EndIf
Debug "4.9"

	; Indicate success.
	bSuccess = #True


	; Free the allocated buffers.
	If pNewAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, pNewAcl) : EndIf
	If psd <> #Null : HeapFree_(GetProcessHeap_(), 0, psd) : EndIf
	If psdNew <> #Null : HeapFree_(GetProcessHeap_(), 0, psdNew) : EndIf

	ProcedureReturn bSuccess

EndProcedure


; Procedure RemoveAceFromWindowStation(hwinsta, psid)
; 
; 	Protected aclSizeInfo.ACL_SIZE_INFORMATION
; 	Protected bDaclExist
; 	Protected bDaclPresent
; 	Protected bSuccess = #False
; 	Protected dwNewAclSize
; 	Protected dwSidSize = 0
; 	Protected dwSdSizeNeeded
; 	Protected pacl
; 	Protected pNewAcl = #Null
; 	Protected psd = #Null
; 	Protected psdNew = #Null
; 	Protected pTempAce.ACCESS_ALLOWED_ACE
; 	#DACL_SECURITY_INFORMATION = 4
; 	Protected si = #DACL_SECURITY_INFORMATION
; 	Protected i
; 
; 	; Obtain the DACL For the window station.
; 	If Not GetUserObjectSecurity_(hwinsta, @si, psd, dwSidSize, @dwSdSizeNeeded)
; 
; 		If GetLastError_() = #ERROR_INSUFFICIENT_BUFFER
; 
; 			psd = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
; 			If psd = #Null : ProcedureReturn 0 : EndIf
; 					
; 			psdNew = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
; 			If psdNew = #Null : ProcedureReturn 0 : EndIf
; 
; 			dwSidSize = dwSdSizeNeeded
; 			If Not GetUserObjectSecurity_(hwinsta, @si, psd, dwSidSize, @dwSdSizeNeeded)
; 				ProcedureReturn 0
; 			EndIf
; 
; 		Else
; 			ProcedureReturn 0
; 		EndIf
; 
; 	EndIf
; 
; 	; Create a new DACL.
; 	If Not InitializeSecurityDescriptor_(psdNew, #SECURITY_DESCRIPTOR_REVISION)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Get the DACL from the security descriptor.
; 	If Not GetSecurityDescriptorDacl_(psd, @bDaclPresent, @pacl, @bDaclExist)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Initialize the ACL.
; 	ZeroMemory_(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION))
; 	aclSizeInfo\AclBytesInUse = SizeOf(ACL) ; ACL ???
; 
; 	; Call only If the DACL is Not NULL.
; 	If pacl <> #Null
; 		; get the file ACL size info
; 		If Not GetAclInformation_(pacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), #AclSizeInformation)
; 			ProcedureReturn 0
; 		EndIf
; 	EndIf
; 
; 	; Compute the size of the new ACL.
; 	dwNewAclSize = aclSizeInfo\AclBytesInUse + (2 * SizeOf(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid_(psid)) - (2 * SizeOf(LONG))
; 
; 	; Allocate memory For the new ACL.
; 	pNewAcl = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwNewAclSize)
; 	If pNewAcl = #Null : ProcedureReturn 0 : EndIf
; 
; 	; Initialize the new DACL.
; 	If Not InitializeAcl_(pNewAcl, dwNewAclSize, #ACL_REVISION)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; If DACL is present, copy it To a new DACL.
; 	If bDaclPresent
; 		; Copy the ACEs To the new ACL.
; 		If aclSizeInfo\AceCount
; 			For i = 0 To aclSizeInfo\AceCount - 1
; 				; Get an ACE.
; 				If Not GetAce_(pacl, i, @pTempAce)
; 					ProcedureReturn 0
; 				EndIf
; 
; 				If Not EqualSid_(psid, @pTempAce\SidStart)
; 					; Add the ACE To the new ACL.
; 					If Not AddAce_(pNewAcl, #ACL_REVISION, #MAXDWORD, pTempAce, pTempAce\Header\AceSize) ; ?????
; 						ProcedureReturn 0
; 					EndIf
; 				EndIf
; 			Next
; 		EndIf
; 	EndIf
; 
; 	If pacl <> #Null : HeapFree_(GetProcessHeap_(), 0, pacl) : EndIf
; 
; 	;Set a new DACL For the security descriptor.
; 	If Not SetSecurityDescriptorDacl_(psdNew, #True, pNewAcl, #False)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Set the new security descriptor For the window station.
; 	If Not SetUserObjectSecurity_(hwinsta, @si, psdNew)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Indicate success.
; 	bSuccess = #True
; 
; 
; 	; Free the allocated buffers.
; 	If pace <> #Null : HeapFree_(GetProcessHeap_(), 0, pace) : EndIf
; 	If pNewAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, pNewAcl) : EndIf
; 	If psd <> #Null : HeapFree_(GetProcessHeap_(), 0, psd) : EndIf
; 	If psdNew <> #Null : HeapFree_(GetProcessHeap_(), 0, psdNew) : EndIf
; 
; 	ProcedureReturn bSuccess
; 
; EndProcedure
; 
; 
; Procedure RemoveAceFromDesktop(hdesk, psid)
; 
; 	Protected aclSizeInfo.ACL_SIZE_INFORMATION
; 	Protected bDaclExist
; 	Protected bDaclPresent
; 	Protected bSuccess = #False
; 	Protected dwNewAclSize
; 	Protected dwSidSize = 0
; 	Protected dwSdSizeNeeded
; 	Protected pacl
; 	Protected pNewAcl = #Null
; 	Protected psd = #Null
; 	Protected psdNew = #Null
; 	Protected pTempAce.ACCESS_ALLOWED_ACE
; 	#DACL_SECURITY_INFORMATION = 4
; 	Protected si = #DACL_SECURITY_INFORMATION
; 	Protected i
; 
; 
; 	; Obtain the DACL For the window station.
; 	If Not GetUserObjectSecurity_(hdesk, @si, psd, dwSidSize, @dwSdSizeNeeded)
; 
; 		If GetLastError_() = #ERROR_INSUFFICIENT_BUFFER
; 
; 			psd = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
; 			If psd = #Null : ProcedureReturn 0 : EndIf
; 					
; 			psdNew = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
; 			If psdNew = #Null : ProcedureReturn 0 : EndIf
; 
; 			dwSidSize = dwSdSizeNeeded
; 			If Not GetUserObjectSecurity_(hdesk, @si, psd, dwSidSize, @dwSdSizeNeeded)
; 				ProcedureReturn 0
; 			EndIf
; 
; 		Else
; 			ProcedureReturn 0
; 		EndIf
; 
; 	EndIf
; 
; 	; Create a new DACL.
; 	If Not InitializeSecurityDescriptor_(psdNew, #SECURITY_DESCRIPTOR_REVISION)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Get the DACL from the security descriptor.
; 	If Not GetSecurityDescriptorDacl_(psd, @bDaclPresent, @pacl, @bDaclExist)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Initialize the ACL.
; 	ZeroMemory_(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION))
; 	aclSizeInfo\AclBytesInUse = SizeOf(ACL) ; ACL ???
; 
; 	; Call only If the DACL is Not NULL.
; 	If pacl <> #Null
; 		; get the file ACL size info
; 		If Not GetAclInformation_(pacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), #AclSizeInformation)
; 			ProcedureReturn 0
; 		EndIf
; 	EndIf
; 
; 	; Compute the size of the new ACL.
; 	dwNewAclSize = aclSizeInfo\AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(psid) - SizeOf(LONG)
; 
; 	; Allocate memory For the new ACL.
; 	pNewAcl = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwNewAclSize)
; 	If pNewAcl = #Null : ProcedureReturn 0 : EndIf
; 
; 	;Initialize the new DACL.
; 	If Not InitializeAcl_(pNewAcl, dwNewAclSize, #ACL_REVISION)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; If DACL is present, copy it To a new DACL.
; 	If bDaclPresent
; 		; Copy the ACEs To the new ACL.
; 		If aclSizeInfo\AceCount
; 			For i = 0 To aclSizeInfo\AceCount - 1
; 				; Get an ACE.
; 				If Not GetAce_(pacl, i, @pTempAce) ; ?????
; 					ProcedureReturn 0
; 				EndIf
; 
; 				If Not EqualSid_(psid, @pTempAce\SidStart)
; 					; Add the ACE To the new ACL.
; 					If Not AddAce_(pNewAcl, #ACL_REVISION, #MAXDWORD, pTempAce, pTempAce\Header\AceSize) ; ?????
; 						ProcedureReturn 0
; 					EndIf
; 				EndIf
; 			Next
; 		EndIf
; 	EndIf
; 
; 	; Set new DACL To the new security descriptor.
; 	If Not SetSecurityDescriptorDacl_(psdNew, #True, pNewAcl, #False)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Set the new security descriptor For the desktop object.
; 	If Not SetUserObjectSecurity_(hdesk, @si, psdNew)
; 		ProcedureReturn 0
; 	EndIf
; 
; 	; Indicate success.
; 	bSuccess = #True
; 
; 
; 	; Free the allocated buffers.
; 	If pAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, pAcl) : EndIf
; 	If pNewAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, pNewAcl) : EndIf
; 	If psd <> #Null : HeapFree_(GetProcessHeap_(), 0, psd) : EndIf
; 	If psdNew <> #Null : HeapFree_(GetProcessHeap_(), 0, psdNew) : EndIf
; 
; 	ProcedureReturn bSuccess
; 
; EndProcedure
; 
; 

;___________________________________________________________________________________________________________________________________________


#TokenPrimary = 1

If Not LogonUser_(@"user", @".", @"pass", #LOGON32_LOGON_INTERACTIVE, #LOGON32_PROVIDER_DEFAULT, @hToken)
	WriteToLog("erreur LogonUser_")
	End
EndIf

AccessWinStation(hToken)
Windows 10 Pro x64
PureBasic 6.04 x64
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: C++ conversion of interactive process

Post by Pierre Bellisle »

Hi tatanas,
Good to read you again.
I did some small modifications to your code.
In the hope that it will help a little...
Double check, I might have missed some points...
Of course, for lurkers, LogonUser name and password have to be set...

Take care,
Pierre

[Complete rewrite using the original C++ source - 2023-02-06]

Code: Select all

EnableExplicit

Structure SID Align 1
  Revision.b
  SubAuthorityCount.b
  Value.b[5] ;or Value.s[6] < SID_IDENTIFIER_AUTHORITY
  SubAuthority.l
EndStructure

Declare.l StartInteractiveClientProcess()
Declare.l AddAceToWindowStation(hWinSta.i, *psid.SID)
Declare.l AddAceToDesktop(hDesk.i, *psid.SID)
Declare.l GetLogonSID(hToken.i, *ppsid.SID)
Declare.l FreeLogonSID(*ppsid.SID)
;_____________________________________________________________________________

;Procedure Hiword(a.w)
; ProcedureReturn(a>>16 & $ffff)
;EndProcedure
;_____________________________________________________________________________

Procedure LOWORD(a.w)
 ProcedureReturn(a & $ffff)
EndProcedure
;_____________________________________________________________________________

Procedure.s WinError(ErrorCode.L)
 Protected *BStrData.String
 Protected *BStr
 Protected ErrorMessage.s
 Protected ErrorLen.l

 ErrorLen = FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM | #FORMAT_MESSAGE_ALLOCATE_BUFFER,
                           #Null$, ErrorCode, #Null$, @*BStr , #Null$, #Null$)
 If ErrorLen
   *BStrData.String = @*BStr
   ErrorMessage = *BStrData\s
   LocalFree_(*BStr)
   ProcedureReturn("Error " + Str(ErrorCode) + " (0x" + Hex(ErrorCode) + ") : " + Left(ErrorMessage, ErrorLen - 2))
 Else
   ProcedureReturn("Unknown error " + Str(ErrorCode) + " (0x" + Hex(ErrorCode) + ")")
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure WriteToLog(entry.s)
 OutputDebugString_(entry) ; Using DebugView from https://learn.microsoft.com/en-us/sysinternals/downloads/debugview
 ;Debug entry
 ;Protected hFile.l
 ;hFile = OpenFile(#PB_Any, "", #PB_File_SharedRead | #PB_File_SharedWrite)
 ;If hFile = #Null
 ;  ProcedureReturn #False
 ;EndIf
 ;FileSeek(hFile, Lof(hFile))
 ;WriteStringN(hFile, entry)
 ;CloseFile(hFile)
 ;ProcedureReturn #True
EndProcedure ; WriteToLog
;_____________________________________________________________________________

Procedure.l StartInteractiveClientProcess()
 Protected pi.PROCESS_INFORMATION
 Protected si.STARTUPINFO
 Protected *pSid.SID
 Protected hToken.i
 Protected hdesk.i
 Protected hwinsta.i
 Protected hwinstaSave.i
 Protected bResult.l

 If (LogonUser_(@"tatanas", @".", @"password", #LOGON32_LOGON_INTERACTIVE, #LOGON32_PROVIDER_DEFAULT, @hToken)) = #False
   MessageBox_(#HWND_DESKTOP, "Set valid logon and password...", "LogonUser", #MB_OK | #MB_SYSTEMMODAL | #MB_TOPMOST)
   Goto Cleanup_StartInteractiveClientProcess
 EndIf

 ; Save a handle To the caller's current window station.
 hwinstaSave = GetProcessWindowStation_()
 If hwinstaSave = #Null : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; Get a handle To the interactive window station.
 hwinsta = OpenWindowStation_(@"winsta0", #False, #READ_CONTROL | #WRITE_DAC)
 If hwinsta = #Null : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; To get the correct Default desktop, set the caller's window station To the interactive window station.
 If SetProcessWindowStation_(hwinsta) = #False : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; Get a handle To the interactive desktop.
 hdesk = OpenDesktop_(@"default", 0, #False, #READ_CONTROL | #WRITE_DAC | #DESKTOP_WRITEOBJECTS | #DESKTOP_READOBJECTS)

 ; Restore the caller's window station.
 If SetProcessWindowStation_(hwinstaSave) = #False : Goto Cleanup_StartInteractiveClientProcess : EndIf

 If hdesk = #Null : Goto Cleanup_StartInteractiveClientProcess : EndIf

 If GetLogonSID(hToken.i, @*pSid.sid) = #False : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; Allow logon SID full access To interactive window station.
 If (AddAceToWindowStation(hwinsta, *pSid)) = #False : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; Allow logon SID full access To interactive desktop.
 If (AddAceToDesktop(hdesk, *pSid)) = #False : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; Impersonate client To ensure access To executable file.
 If (ImpersonateLoggedOnUser_(hToken)) = #False : Goto Cleanup_StartInteractiveClientProcess : EndIf

 ; Initialize the STARTUPINFO Structure.
 ; Specify that the process runs IN the interactive desktop.
 ZeroMemory_(@si, SizeOf(STARTUPINFO))
 si\cb = SizeOf(STARTUPINFO)
 si\lpDesktop = @"WinSta0\Default" ;si.lpDesktop = TEXT("winsta0\\default");

 bResult = CreateProcessAsUser_(hToken, #Null, @"c:\windows\system32\notepad.exe", #Null, #Null, #False,
                                #NORMAL_PRIORITY_CLASS | #CREATE_NEW_CONSOLE, #Null, #Null, @si, @pi)
 ; End impersonation of client.
 RevertToSelf_()

 If (bResult <> #False And pi\hProcess <> #INVALID_HANDLE_VALUE)
   WaitForSingleObject_(pi\hProcess, #INFINITE)
   CloseHandle_(pi\hProcess)
 EndIf

 Cleanup_StartInteractiveClientProcess:
   If hwinstaSave <> #Null : SetProcessWindowStation_(hwinstaSave) : EndIf

   ; Free the buffer for the logon SID
   If *pSid <> #Null : FreeLogonSID(*pSid) : EndIf

   ; Close the handles To the interactive window station And desktop.
   If hwinsta <> #Null : CloseWindowStation_(hwinsta) : EndIf
   If hdesk   <> #Null : CloseDesktop_(hdesk)         : EndIf

   ; Close the handle To the client's access token.
   If hToken <> #INVALID_HANDLE_VALUE : CloseHandle_(hToken) : EndIf

 ProcedureReturn(bResult)

EndProcedure ; StartInteractiveClientProcess(hToken)
;_____________________________________________________________________________

Procedure.l GetLogonSID(hToken.i, *ppsid.SID)
 Protected *ptg.TOKEN_GROUPS
 Protected *psid.SID
 Protected bSuccess.l
 Protected dwIndex.l
 Protected dwLength.l

 ; Verify the parameter passed IN is Not NULL.
 If *ppsid = #Null : Goto Cleanup_GetLogonSID : EndIf

 ; Get required buffer size And allocate the TOKEN_GROUPS buffer.
 GetTokenInformation_(hToken, #TokenGroups, 0, 0, @dwLength)
 If GetLastError_() <> #ERROR_INSUFFICIENT_BUFFER : Goto Cleanup_GetLogonSID : EndIf

 *ptg = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwLength)
 If *ptg = #Null : Goto Cleanup_GetLogonSID : EndIf

 ; Get the token group information from the access token.
 If (GetTokenInformation_(hToken, #TokenGroups, *ptg, dwLength, @dwLength)) = #False : Goto Cleanup_GetLogonSID : EndIf

 ; Loop through the groups To find the logon SID.
 For dwIndex = 0 To *ptg\GroupCount - 1
   If *ptg\Groups[dwIndex]\Attributes & #SE_GROUP_LOGON_ID = #SE_GROUP_LOGON_ID
     ; Found the logon SID; make a copy of it.
     dwLength = GetLengthSid_(*ptg\Groups[dwIndex]\Sid)
     *psid = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwLength)
     If *psid = #Null : Goto Cleanup_GetLogonSID : EndIf
     If CopySid_(dwLength, *psid, *ptg\Groups[dwIndex]\Sid) = #False
       HeapFree_(GetProcessHeap_(), 0, *psid)
       Goto Cleanup_GetLogonSID
     EndIf
     PokeI(*ppsid, *psid) ;Copy *psid to *ppsid
     bSuccess = #True
     Break
   EndIf
 Next

Cleanup_GetLogonSID:
 If *ptg <> #Null : HeapFree_(GetProcessHeap_(), 0, *ptg) : EndIf

 ProcedureReturn bSuccess

EndProcedure ;GetLogonSID
;_____________________________________________________________________________

Procedure.l FreeLogonSID(*ppsid.SID)

 HeapFree_(GetProcessHeap_(), 0, *ppsid)

EndProcedure ;FreeLogonSID
;_____________________________________________________________________________

Procedure.l AddAceToWindowStation(hwinsta.i, *psid.sid)
 #DACL_SECURITY_INFORMATION = 4
 Protected *pace.ACCESS_ALLOWED_ACE
 Protected aclSizeInfo.ACL_SIZE_INFORMATION
 Protected *pNewAcl.SECURITY_DESCRIPTOR
 Protected *psd.SECURITY_DESCRIPTOR
 Protected *psdNew.SECURITY_DESCRIPTOR
 Protected *pTempAce.ACE_HEADER
 Protected *aceHeader.ACE_HEADER
 Protected pacl.i
 Protected bDaclExist.l
 Protected bSuccess.l
 Protected bDaclPresent.l
 Protected dwNewAclSize.l
 Protected dwSidSize.l
 Protected dwSdSizeNeeded.l
 Protected i.l
 Protected si.l = #DACL_SECURITY_INFORMATION

 ; Obtain the DACL For the window station.
 If (GetUserObjectSecurity_(hwinsta, @si, *psd, dwSidSize, @dwSdSizeNeeded)) = #False

  If GetLastError_() = #ERROR_INSUFFICIENT_BUFFER

    *psd = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
    If *psd = #Null : Goto Cleanup_AddAceToWindowStation : EndIf

    *psdNew = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
    If *psdNew = #Null : Goto Cleanup_AddAceToWindowStation : EndIf

    dwSidSize = dwSdSizeNeeded
    If (GetUserObjectSecurity_(hwinsta, @si, *psd, dwSidSize, @dwSdSizeNeeded)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

  Else
    Goto Cleanup_AddAceToWindowStation
  EndIf
 EndIf

 ; Create a new DACL.
 If (InitializeSecurityDescriptor_(*psdNew, #SECURITY_DESCRIPTOR_REVISION)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ; Get the DACL from the security descriptor.
 If (GetSecurityDescriptorDacl_(*psd, @bDaclPresent, @pacl, @bDaclExist)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ; Initialize the ACL.
 ZeroMemory_(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION))
 aclSizeInfo\AclBytesInUse = SizeOf(ACL)

 ; Call only If the DACL is Not NULL.
 If pacl <> #Null
   ; get the file ACL size info
   If (GetAclInformation_(pacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), #AclSizeInformation)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf
 EndIf

 ; Compute the size of the new ACL.
 dwNewAclSize = aclSizeInfo\AclBytesInUse + (2 * SizeOf(ACCESS_ALLOWED_ACE)) + (2 * GetLengthSid_(*psid)) - (2 * SizeOf(LONG))

 ; Allocate memory For the new ACL.
 *pNewAcl = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwNewAclSize)
 If *pNewAcl = #Null : Goto Cleanup_AddAceToWindowStation : EndIf

 ; Initialize the new DACL.
 If (InitializeAcl_(*pNewAcl, dwNewAclSize, #ACL_REVISION)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ; If DACL is present, copy it To a new DACL.
 If bDaclPresent
   ; Copy the ACEs To the new ACL.
   If aclSizeInfo\AceCount
     For i = 0 To aclSizeInfo\AceCount - 1

       ; Get an ACE.
       If (GetAce_(pacl, i, @*pTempAce)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

       ; Add the ACE To the new ACL.
       If (AddAce_(*pNewAcl, #ACL_REVISION, #MAXDWORD, *pTempAce, *pTempAce\AceSize)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

     Next
   EndIf
 EndIf

 #GENERIC_ACCESS = #GENERIC_READ | #GENERIC_WRITE | #GENERIC_EXECUTE | #GENERIC_ALL
 #WINSTA_ALL = #WINSTA_ENUMDESKTOPS | #WINSTA_READATTRIBUTES | #WINSTA_ACCESSCLIPBOARD | #WINSTA_CREATEDESKTOP |
               #WINSTA_WRITEATTRIBUTES | #WINSTA_ACCESSGLOBALATOMS | #WINSTA_EXITWINDOWS | #WINSTA_ENUMERATE |
               #WINSTA_READSCREEN | #STANDARD_RIGHTS_REQUIRED

 ;Add the first ACE To the window station.
 *pace = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(*psid) - SizeOf(LONG))
 If *pace = #Null : Goto Cleanup_AddAceToWindowStation : EndIf

 *pace\Header\AceType  = #ACCESS_ALLOWED_ACE_TYPE
 *pace\Header\AceFlags = #CONTAINER_INHERIT_ACE | #INHERIT_ONLY_ACE | #OBJECT_INHERIT_ACE
 *pace\Header\AceSize  = LOWORD(SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(*psid) - SizeOf(LONG))
 *pace\Mask            = #GENERIC_ACCESS

 If (CopySid_(GetLengthSid_(*psid), @*pace\SidStart, *psid)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 If (AddAce_(*pNewAcl, #ACL_REVISION, #MAXDWORD, *pace, *pace\Header\AceSize)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ; Add the second ACE To the window station.
 *pace\Header\AceFlags = #NO_PROPAGATE_INHERIT_ACE
 *pace\Mask            = #WINSTA_ALL

 ;AddAce adds one or more access control entries (ACEs) to a specified access control list (ACL).
 If (AddAce_(*pNewAcl, #ACL_REVISION, #MAXDWORD, *pace,    *pace\Header\AceSize)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ;Set a new DACL For the security descriptor.
 If (SetSecurityDescriptorDacl_(*psdNew, #True, *pNewAcl, #False)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ; Set the new security descriptor For the window station.
 If (SetUserObjectSecurity_(hwinsta, @si, *psdNew)) = #False : Goto Cleanup_AddAceToWindowStation : EndIf

 ; Indicate success.
 bSuccess = #True

Cleanup_AddAceToWindowStation:
 ; Free the allocated buffers.
 If *pace    <> #Null : HeapFree_(GetProcessHeap_(), 0, *pace)    : EndIf
 If *pNewAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, *pNewAcl) : EndIf
 If *psd     <> #Null : HeapFree_(GetProcessHeap_(), 0, *psd)     : EndIf
 If *psdNew  <> #Null : HeapFree_(GetProcessHeap_(), 0, *psdNew)  : EndIf

 ProcedureReturn bSuccess

EndProcedure ;AddAceToWindowStation
;_____________________________________________________________________________

Procedure.l AddAceToDesktop(hdesk, psid)
 #DACL_SECURITY_INFORMATION = 4
 Protected aclSizeInfo.ACL_SIZE_INFORMATION
 Protected *pNewAcl.ACL
 Protected *psd.SECURITY_DESCRIPTOR
 Protected *psdNew.SECURITY_DESCRIPTOR
 Protected *pTempAce.ACE_HEADER
 Protected pacl.i
 Protected bDaclExist.l
 Protected bDaclPresent.l
 Protected bSuccess.l
 Protected dwNewAclSize.l
 Protected dwSidSize.l
 Protected dwSdSizeNeeded.l
 Protected i.l
 Protected si = #DACL_SECURITY_INFORMATION

 ;Obtain the security descriptor For the desktop object.
 If (GetUserObjectSecurity_(hdesk, @si, *psd, dwSidSize, @dwSdSizeNeeded)) = #False

   If GetLastError_() = #ERROR_INSUFFICIENT_BUFFER

     *psd = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
     If *psd = #Null : Goto Cleanup_AddAceToDesktop : EndIf

     *psdNew = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwSdSizeNeeded)
     If *psdNew = #Null : Goto Cleanup_AddAceToDesktop : EndIf

     dwSidSize = dwSdSizeNeeded
     If GetUserObjectSecurity_(hdesk, @si, *psd, dwSidSize, @dwSdSizeNeeded) = #False : Goto Cleanup_AddAceToDesktop : EndIf
   Else
     Goto Cleanup_AddAceToDesktop
   EndIf
 EndIf

 ;Create a new security descriptor
 If InitializeSecurityDescriptor_(*psdNew, #SECURITY_DESCRIPTOR_REVISION) = #False : Goto Cleanup_AddAceToDesktop : EndIf

 ; Obtain the DACL from the security descriptor.
 If GetSecurityDescriptorDacl_(*psd, @bDaclPresent, @pacl, @bDaclExist) = #False : Goto Cleanup_AddAceToDesktop : EndIf

 ; Initialize the ACL.
 ZeroMemory_(@aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION))
 aclSizeInfo\AclBytesInUse = SizeOf(ACL)

 ; Call only If the DACL is Not NULL.
 If pacl <> #Null
   ; Determine the size of the ACL information
   If GetAclInformation_(pacl, @aclSizeInfo, SizeOf(ACL_SIZE_INFORMATION), #AclSizeInformation) = #False : Goto Cleanup_AddAceToDesktop : EndIf
 EndIf

 ; Compute the size of the new ACL.
 dwNewAclSize = aclSizeInfo\AclBytesInUse + SizeOf(ACCESS_ALLOWED_ACE) + GetLengthSid_(psid) - SizeOf(LONG)

 ; Allocate memory For the new ACL.
 *pNewAcl = HeapAlloc_(GetProcessHeap_(), #HEAP_ZERO_MEMORY, dwNewAclSize)
 If *pNewAcl = #Null : Goto Cleanup_AddAceToDesktop : EndIf

 ;Initialize the new DACL.
 If InitializeAcl_(*pNewAcl, dwNewAclSize, #ACL_REVISION) = #False : Goto Cleanup_AddAceToDesktop : EndIf

 ; If DACL is present, copy it To a new DACL.
 If bDaclPresent

  ; Copy the ACEs To the new ACL.
  If aclSizeInfo\AceCount
   For i = 0 To aclSizeInfo\AceCount - 1

    ; Get an ACE.
    If GetAce_(pacl, i, @*pTempAce) = #False : Goto Cleanup_AddAceToDesktop : EndIf

    ; Add the ACE To the new ACL.
    If AddAce_(*pNewAcl, #ACL_REVISION, #MAXDWORD, *pTempAce, *pTempAce\AceSize) = #False : Goto Cleanup_AddAceToDesktop : EndIf

   Next
  EndIf

 EndIf

 #DESKTOP_ALL = #DESKTOP_READOBJECTS | #DESKTOP_CREATEWINDOW | #DESKTOP_CREATEMENU | #DESKTOP_HOOKCONTROL | #DESKTOP_JOURNALRECORD |
                #DESKTOP_JOURNALPLAYBACK | #DESKTOP_ENUMERATE | #DESKTOP_WRITEOBJECTS | #DESKTOP_SWITCHDESKTOP | #STANDARD_RIGHTS_REQUIRED

 ; Add ACE To the DACL.
 If AddAccessAllowedAce_(*pNewAcl, #ACL_REVISION, #DESKTOP_ALL, psid) = #False : Goto Cleanup_AddAceToDesktop : EndIf

 ; Set new DACL To the new security descriptor.
 If SetSecurityDescriptorDacl_(*psdNew, #True, *pNewAcl, #False) = #False : Goto Cleanup_AddAceToDesktop : EndIf

 ; Set the new security descriptor For the desktop object.
 If SetUserObjectSecurity_(hdesk, @si, *psdNew) = #False : Goto Cleanup_AddAceToDesktop : EndIf

 ; Indicate success.
 bSuccess = #True

Cleanup_AddAceToDesktop:
 ; Free the allocated buffers.
 If *pNewAcl <> #Null : HeapFree_(GetProcessHeap_(), 0, *pNewAcl) : EndIf
 If *psd     <> #Null : HeapFree_(GetProcessHeap_(), 0, *psd)     : EndIf
 If *psdNew  <> #Null : HeapFree_(GetProcessHeap_(), 0, *psdNew)  : EndIf

 ProcedureReturn bSuccess

EndProcedure ;AddAceToDesktop
;_____________________________________________________________________________

Procedure MainProc()

 ;Protected zFileName.s
 ;zFileName.s = Space(#MAX_PATH)
 ;GetModuleFileName_(0, zFileName, #MAX_PATH)
 ;WriteToLog(RTrim(zFileName))

 StartInteractiveClientProcess()

EndProcedure ;Main
;_____________________________________________________________________________

MainProc()
;_____________________________________________________________________________
;
; IDE Options = PureBasic 5.73 LTS (Windows - x64)
; CursorPosition = 99
; FirstLine = 47
; EnableAsm
; EnableXP
; Executable = InteractiveClientProcessWeb3.exe
; DisableDebugger
; CompileSourceDirectory
Last edited by Pierre Bellisle on Tue Feb 07, 2023 4:29 am, edited 8 times in total.
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: C++ conversion of interactive process

Post by tatanas »

Thanks Pierre :D
Glad you were able to look at this code. But I have an error when I try to run it : "The debugged executable quit unexpectedly".
No further indication...

EDIT :
No more error if I replace OutputDebugString_() by Debug() but the notepad.exe doesn't start.
Same 1314 error from CreateProcessAsUser_()
Windows 10 Pro x64
PureBasic 6.04 x64
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: C++ conversion of interactive process

Post by Pierre Bellisle »

Hey,
On my side, I thought OutputDebugString_ reported only successfull action.
I missed the CreateProcessAsUser_() error.

Not sure about the Debug(), maybe some conflict since this code goes goes deep in some aspect.
As a try, you may download Sysinternals's debugview

I may have some time today, with your comment in mind, I will see if I can do some corrections...
Specially for CreateProcessAsUser_()

Added: Error 1314 (0x522) : Le client ne dispose pas d’un privilège nécessaire.
Added: Error 1314: A required privilege is not held by the client. (ERROR_PRIVILEGE_NOT_HELD 0x522)
Added: This is quite a good indication...

The CreateProcessAsUser Microsoft web page have a remark about error 1314...
Solution is near, get SE_INCREASE_QUOTA_NAME privilege or use CreateProcessWithLogonW().

Regards,
Pierre
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: C++ conversion of interactive process

Post by Pierre Bellisle »

I got this working... Still not enough... Going back to the drawing board...

Code: Select all

 Protected TokenPriv.TOKEN_PRIVILEGES
 TokenPriv\PrivilegeCount = 1
 LookupPrivilegeValue_(#Null, "SeIncreaseQuotaPrivilege", TokenPriv\Privileges[0]\Luid) ;SE_INCREASE_QUOTA_NAME = "SeIncreaseQuotaPrivilege"
 TokenPriv\Privileges[0]\Attributes = 2 ;SE_PRIVILEGE_ENABLED = 2
 If AdjustTokenPrivileges_(hToken, FALSE, TokenPriv, 0, #Null, #Null) ;nzok	
    WriteToLog("a18a AdjustTokenPrivileges_: ok")
 Else
    WriteToLog("a18a AdjustTokenPrivileges_: failed")
 EndIf
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: C++ conversion of interactive process

Post by Pierre Bellisle »

In my context, i didn't have success with CreateProcessAsUser_() even with SeIncreaseQuotaPrivilege and SeAssignPrimaryTokenPrivilege privileges.
Still more work to do on this.

I replaced CreateProcessAsUser_() with CreateProcessWithLogonW() and it seems to work fine...
As always, LogonUser_ and CreateProcessWithLogonW need username and password set.

Code in my first post has been updated.

Hope it work fine on your side...
Pierre

[Added: I did a correction in Procedure FreeLogonSID]
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: C++ conversion of interactive process

Post by tatanas »

IT's ok for me too. Nice job ! You really understand the internal mechanics of windows.
And I'm glad that all the code, except the CreateProcessAsUser part, I converted from C++ is not buggy. It was not easy...

Again, thank you very much for helping me.


EDIT : Is there a problem with the FreeLogonSID procedure ? The paramater is a pointer (*ppsid) but the variable used inside is different : "ppsid" (=0)

Code: Select all

Procedure FreeLogonSID(*ppsid) 
  WriteToLog("f1 FreeLogonSID start") ;nzok
   If HeapFree_(GetProcessHeap_(), 0, ppsid)  
     WriteToLog("f1 FreeLogonSID ok") ;nzok
   Else
     WriteToLog("f1 FreeLogonSID failed") ;nzok
   EndIf
EndProcedure ;FreeLogonSID
EDIT 2 : Too bad, it's not working when the code is executed as system (from a service). I've got ERROR_ACCESS_DENIED from CreateProcessWithLogonW() and a crash because of the FreeLogonSID() procedure.
From mdsn :
Windows XP with SP2,Windows Server 2003, or later: You cannot call CreateProcessWithLogonW from a process that is running under the "LocalSystem" account, because the function uses the logon SID in the caller token, and the token for the "LocalSystem" account does not contain this SID. As an alternative, use the CreateProcessAsUser and LogonUser functions.
EDIT3 : I used PaExec source code (opensource) to create this sample but it seems that it is not working either. As for PSExec, it works correctly (no source :( )
Windows 10 Pro x64
PureBasic 6.04 x64
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: C++ conversion of interactive process

Post by Pierre Bellisle »

About FreeLogonSID, I had some GPF, the absence of "*" is the left over result of some try I did.
It's now set back to a *Pointer as it should.

About CreateProcessWithLogonW, I'm with you, I went back to CreateProcessAsUser.
Before calling CreateProcessAsUser I call AdjustTokenPrivileges with SeIncreaseQuotaPrivilege
and SeAssignPrimaryTokenPrivilege, from my context, it seems it was not mandatory,
I leaved it there, nothing to loose, try as you like.

I updated the code from my first post.
As a Local SystemAdministrator, I can execute the code and get Notepad to appear.

Hope it will be fine on your side.
Pierre
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: C++ conversion of interactive process

Post by tatanas »

As a Local SystemAdministrator, I can execute the code and get Notepad to appear.
What do you mean ? When the code is executed from a service, you get the notepad gui ? So a notepad.exe in session X (not 0) executed by the Username you provided in parameter ?
Because on my side, it's the same as before, I've got a notepad in session 0 (without GUI) executed by the User provided in the Logon() paramater.
And FreeLogonSID() craches the service.
If I run the code on local machine (no service), I've got a Error 1314 from CreateProcessAsUser.
Too bad the PsExec source is not available...

EDIT : This is the best I can achieve (a black gui of notepad)
Insert this code before the ImpersonateLoggedOnUser_()

Code: Select all

	#TokenSessionId = 12
	#SecurityImpersonation = 2
	#TokenPrimary = 1

	Protected ReturnLength ; semble retourner un LONG
	Protected targetSessionID.l = WTSGetActiveConsoleSessionId()
	Protected origSessionID.l
	Protected error

	If Not IPF_EnablePrivilege("SeDebugPrivilege")
		WriteToLog("Erreur IPF_EnablePrivilege")
	EndIf
	If Not IPF_EnablePrivilege("SeIncreaseQuotaPrivilege")
		WriteToLog("Erreur SeIncreaseQuotaPrivilege")
	EndIf
	If Not IPF_EnablePrivilege("SeAssignPrimaryTokenPrivilege")
		WriteToLog("Erreur SeAssignPrimaryTokenPrivilege")
	EndIf

	Protected ret = GetTokenInformation_(hToken, #TokenSessionId, #Null, 0, @ReturnLength)
	If ret = 0
		error = GetLastError_()
		If error = #ERROR_INSUFFICIENT_BUFFER

			; on laisse faire, c'est normal, ReturnLength contient ce qu'il faut maintenant
			Protected *TokenInformation ;= AllocateMemory(ReturnLength)
			*TokenInformation = LocalAlloc_(#LMEM_FIXED, ReturnLength)
			
			If *TokenInformation <> #Null
				If GetTokenInformation_(hToken, #TokenSessionId, @*TokenInformation, ReturnLength, @ReturnLength)
					origSessionID = PeekL(@*TokenInformation)

					If Not IPF_EnablePrivilege("SeTcbPrivilege", #True, hToken) ;SE_TCB_NAME
						WriteToLog("Erreur SeTcbPrivilege")
					EndIf
				
					PokeL(@*TokenInformation, targetSessionID)
					WriteToLog("targetSessionID="+targetSessionID)

					If Not SetTokenInformation_(hToken, #TokenSessionId, @*TokenInformation, ReturnLength)
						error = GetLastError_()
						WriteToLog("SetTokenInformation_ 1 : " + WinError(error))
					EndIf

				Else
					error = GetLastError_()
					WriteToLog("GetTokenInformation_ 2 : " + error)
				EndIf

; 				LocalFree_(*TokenInformation)
			EndIf
			
		Else
			WriteToLog("GetTokenInformation_ 1 : " + error)
			WriteToLog("ReturnLength=" + ReturnLength)
		EndIf
	EndIf
EDIT 2 : I just thought of something, all the functions GetProcessWindowStation/OpenWindowStation/SetProcessWindowStation/SetProcessWindowStation aren't they working on the "session 0" Winstation ? If so then it should be on user session X ?
Windows 10 Pro x64
PureBasic 6.04 x64
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: C++ conversion of interactive process

Post by JHPJHP »

Hi tatanas,

I tried the latest version posted by Pierre Bellisle, and it worked as expected.
I compiled the code into an executable, ran it from the NT Authority\System account and Notepad opened.

Are you trying to run an Interactive Process from a Windows Service? If not, what are your requirements?

Previously, I wrote my own example from the link you posted, and the code worked as intended.
Last edited by JHPJHP on Tue Feb 07, 2023 5:25 am, edited 1 time in total.
User avatar
Pierre Bellisle
User
User
Posts: 35
Joined: Wed Jun 27, 2018 5:12 am

Re: C++ conversion of interactive process

Post by Pierre Bellisle »

tatanas: Too bad the PsExec source is not available...

A quick note: Are you aware of PAExec source, clone of PsExec

Added: Code in my first post was updated with EnableExplicit. I did set the type of all variables and made some more modifications. Program should be more robust, without the FreeLogonSID() GPF.
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: C++ conversion of interactive process

Post by tatanas »

I'm sorry I can't get it to work.
I included your code in my service (your service code too :) ). So it is executed by system from session 0.
But notepad start in session 0 not the user session who is currently logged in.
Here is the log :
a1 AccessWinStation start
a3 hWinStaSave 0x9C
a5 hWinSta 0x458
a7 hDesk 0x454
a9 Get Default desktop 0x454
g1 hToken 0x420 *ppsid = 0x176FCD8
g2 hToken 0x420
g3 TokenInformation meeded len 44
g4 ptu AllocateMemory 0x1F0770 len = 44
g5 ptu AllocateMemory 0x1F0770 dwLength = 44 / 44
g6 *ppsid 0x176FCD8 *pTU\User\Sid 0x1F0780
g7 CopySid_ success
g9 *ppsid 0x176FCD8 *pTU\User\Sid 0x1F0780
g10 GetTokenInformation_ ok
g12 ptu AllocateMemory 0x1F0770 len = 44
g14 ptu AllocateMemory 0x1F0770 len = 44
g16 *ptg GetTokenInformation_ OK 408
g17 ptg GetTokenInformation_ ok
g18 ptg AllocateMemory 0x1F2FA0 len = 408
g19 ptg\GroupCount = 13
g20 ptg\GroupCount # 0
g20 ptg\GroupCount # 1
g20 ptg\GroupCount # 2
g20 ptg\GroupCount # 3
g20 ptg\GroupCount # 4
g20 ptg\GroupCount # 5
g20 ptg\GroupCount # 6
g20 ptg\GroupCount # 7
g20 ptg\GroupCount # 8
g20 ptg\GroupCount # 9
g20 ptg\GroupCount # 10
g21 ptg Found the logon SID
g23 ptu AllocateMemory 0x1F0770 len = 20
g24 Cleanup_GetLogonSID
g25 ptu FreeMemory 0x1F0770
g26 ptg FreeMemory 0x0
g27 GetLogonSID exit 1
a11 GetLogonSID ok
w1 AddAceToWindowStation
w2 GetUserObjectSecurity_
w3 InitializeSecurityDescriptor_
w4 GetSecurityDescriptorDacl_
w5 GetAclInformation_
w6 dwNewAclSize
w7 bDaclPresent
w8 HeapAlloc_
w9 AceType
w10 AddAce_
w11 SetUserObjectSecurity_
w99 exit
a13 AddAceToWindowStation ok
k1 AddAceToDesktop
k2 GetUserObjectSecurity_
k3 InitializeSecurityDescriptor_
k4 GetSecurityDescriptorDacl_
k5 GetAclInformation_
k6 dwNewAclSize
k7 HeapAlloc_
k8 bDaclPresent
k9 AddAccessAllowedAce_
k99 exit
a15 AddAceToDesktop ok
a16 ImpersonateLoggedOnUser_ ok
a18 AdjustTokenPrivileges: Success if non zero = 1
LastError = Error 1300 (0x514) : L’appelant ne bénéficie pas de tous les privilèges ou groupes référencés.
a19 CreateProcessAsUser: ok
a99 AccessWinStation exitting
It seems the AdjustTokenPrivileges_ is at fault.

About PaExec :
EDIT3 : I used PaExec source code (opensource) to create this sample but it seems that it is not working either. As for PSExec, it works correctly (no source :( )
I posted a message in the PaExec forum yesterday : http://support.poweradmin.com/osqa/ques ... age=1#3528
Windows 10 Pro x64
PureBasic 6.04 x64
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: C++ conversion of interactive process

Post by tatanas »

Hi JHPJHP,

Do you execute it from a service in session 0 ?
Windows 10 Pro x64
PureBasic 6.04 x64
JHPJHP
Addict
Addict
Posts: 2129
Joined: Sat Oct 09, 2010 3:47 am
Contact:

Re: C++ conversion of interactive process

Post by JHPJHP »

Hi tatanas,
JHPJHP wrote:Are you trying to run an Interactive Process from a Windows Service?
You didn't answer my question. I asked it because included in my Windows Services & Other Stuff download is script to create and run a service as the logged-in user without having to provide login credentials.

It doesn't make sense providing login credentials (at run time) for a script to run from a service. If you're already providing user credentials, then why not just create the service to run in the user's session?

I'm not sure what happened between yesterday and today, but the script provided by Pierre Bellisle is no longer working.
I might have been mistaken about it working yesterday, I did not test what session notepad was running in.

To answer your question, yes, I run my version of the script from a Session 0 account (NT Authority\System), executing Command Prompt (CMD). When I type WHOAMI into the window it returns the logged-in user account.
tatanas
Enthusiast
Enthusiast
Posts: 199
Joined: Wed Nov 06, 2019 10:28 am
Location: France

Re: C++ conversion of interactive process

Post by tatanas »

I just want to do the same as psexec.
I work on a school environnement. So I try to order remotely students/professors computers.
The service is actualy a tcp client which can respond to server commands. One of the command could be the execution of a program on this client with admin rights (the one provided by my remote command). This can permit interactiv (or silent) installation of program.
I'm already able to execute program as system (too much rights) or as the logged on user (not enough rights).
Windows 10 Pro x64
PureBasic 6.04 x64
Post Reply