$theTitle=wp_title(" - ", false); if($theTitle != "") { ?>
About Virtualization, VDI, SBC, Application Compatibility and anything else I feel like
3 Mar // php the_time('Y') ?>
In the previous parts (part 1 part 2) i’ve described the theoretical part and implementation problems. So, now we can write the code:
1) In case we login the user, we just call LsaLogonUser to get the token:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | function CopyString(const Source : WideString; const Buffer : Pointer; var CurrentPosition : Pointer; LengthInBytes: Cardinal = $FFFFFFFF) : Pointer; begin if (Source <> '' ) then begin Cardinal(Result) := Cardinal(CurrentPosition) - Cardinal(Buffer); if (LengthInBytes = $FFFFFFFF) then LengthInBytes := (Length(Source) + 1) * SizeOf(Source[1]); CopyMemory(CurrentPosition, PWideChar(Source), LengthInBytes); Cardinal(CurrentPosition) := Cardinal(CurrentPosition) + LengthInBytes; end else Result := nil; end; procedure CopyUnicodeString(const Target : PUnicodeString; const Source : PUnicodeString; const Buffer : Pointer; var CurrentPosition : Pointer); begin Target^ := Source^; Target^.Buffer := CopyString(Target^.Buffer, Buffer, CurrentPosition, Target^.MaximumLength); end; procedure CreateLsaLogonPackageBuffer(const UserName, Domain, Password : WideString; out Result : PMSV1_0_INTERACTIVE_LOGON; out ResultLength : Cardinal); var LogonCurrentPointer : Pointer; begin ResultLength := SizeOf(Result^) + (Length(UserName) + Length(Domain) + Length(Password) + 3) * SizeOf(UserName[1]); Result := GetMemory(ResultLength); try ZeroMemory(Result, ResultLength); Cardinal(LogonCurrentPointer) := Cardinal(Result) + SizeOf(Result^); Result^.MessageType := MsV1_0InteractiveLogon; RtlInitUnicodeString(@Result^.UserName, PWideChar(UserName)); CopyUnicodeString(@Result^.UserName, @Result^.UserName, nil, LogonCurrentPointer); RtlInitUnicodeString(@Result^.LogonDomainName, PWideChar(Domain)); CopyUnicodeString(@Result^.LogonDomainName, @Result^.LogonDomainName, nil, LogonCurrentPointer); RtlInitUnicodeString(@Result^.Password, PWideChar(Password)); CopyUnicodeString(@Result^.Password, @Result^.Password, nil, LogonCurrentPointer); except FreeMemory(Result); Raise; end; end; procedure LoginUser; var LsaHandle : THandle; LsaName : LSA_STRING; PackageName : LSA_STRING; AuthenticationPackage : ULONG; LogonPackageBuffer : PMSV1_0_INTERACTIVE_LOGON ; LogonPackageBufferLength : Cardinal; TokenSource : JwaWindows.TTokenSource; SubStatus : Integer; SecurityMode : LSA_OPERATIONAL_MODE; begin RtlInitUnicodeString(@LsaName, 'AutoLogonXP login'); NtCheck(LsaRegisterLogonProcess(LsaName, LsaHandle, @SecurityMode)); try RtlInitString(@PackageName, PCSZ(PChar(MSV1_0_PACKAGE_NAME))); LsaLookupAuthenticationPackage(LsaHandle, PackageName, AuthenticationPackage); JwaWindows.AllocateLocallyUniqueId(TokenSource.SourceIdentifier); TokenSource.SourceName := 'AUTOLGON'; CreateLsaLogonPackageBuffer(UserName, Domain, Password, LogonPackageBuffer, LogonPackageBufferLength); try NtCheck( LsaLogonUser( LsaHandle, LsaName, Interactive, AuthenticationPackage, LogonPackageBuffer, LogonPackageBufferLength, TokenGroups, @TokenSource, Pointer(Profile), ProfileLength, JwaWindows.LUID(Result^.LogonId), Result^.UserToken, JwaWindows.QUOTA_LIMITS(Result^.Quotas), SubStatus )); finally FreeLsaLogonPackageBuffer(LogonPackageBuffer); end; finally LsaDeregisterLogonProcess(LsaHandle); end; end; |
2) In case we need to create a token, we call NtCreateToken. If there are no groups, we create a single group with the Primary SID in this group :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | procedure CreateToken; var ExpirationTime : LARGE_INTEGER; User : JwaWindows.TOKEN_USER; TokenSource : TOKEN_SOURCE; PrimaryGroup : JwaWindows.TOKEN_PRIMARY_GROUP; EmptyTokenGroups : JwaWindows.TTokenGroups; begin ExpirationTime.QuadPart := 0; User.User.Sid := PrimarySID; User.User.Attributes := 0; TokenSource.SourceName := 'AUTOLGON'; Win32Check(AllocateLocallyUniqueId(TokenSource.SourceIdentifier)); Result^.LogonId := SYSTEM_LUID; PrimaryGroup.PrimaryGroup := PrimarySID; if (nil = TokenGroups) then begin EmptyTokenGroups.GroupCount := 1; EmptyTokenGroups.Groups[0].Sid := PrimarySID; EmptyTokenGroups.Groups[0].Attributes := SE_GROUP_MANDATORY or SE_GROUP_ENABLED or SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_LOGON_ID; TokenGroups := @EmptyTokenGroups; end; EnableToken(SE_CREATE_TOKEN_NAME); NtCheck( NtCreateToken( @Result^.UserToken, TOKEN_ALL_ACCESS, nil, TokenPrimary, @Result^.LogonId, @ExpirationTime, @User, TokenGroups, Privs, nil, @PrimaryGroup, nil, @TokenSource )); try Profile := AllocEmptyProfile(ProfileLength); except CloseHandle(Result^.UserToken); Raise; end; end; |
3) In case we want to use a “current” system token, we need to do some adjustments with it – we need to add the group with SE_GROUP_LOGON_ID flag to it:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 | function GetFromToken(TokenHandle : THandle; Info : TOKEN_INFORMATION_CLASS) : Pointer; var Res : DWORD; begin Result := nil; GetTokenInformation(TokenHandle, info, nil, 0, Res); if GetLastError = ERROR_INSUFFICIENT_BUFFER then begin Result := GetMemory(Res); Win32Check(GetTokenInformation(TokenHandle, info, Result, Res, Res)); end else RaiseLastOSError; end; procedure AsSystem; var SystemToken : THandle; Statistics : PTokenStatistics; User : JwaWinNT.PTokenUser; Priviledges : JwaWindows.PTokenPrivileges; Owner : PTokenOwner; Groups : JwaWindows.PTokenGroups; PrimaryGroup : PTokenPrimaryGroup; DefaultDacl : PTokenDefaultDacl; TokenSrc : PTokenSource; TokenNewGroups : JwaWindows.PTokenGroups; I : Integer; begin Win32Check(OpenProcessToken(GetCurrentProcess, TOKEN_ALL_ACCESS, SystemToken)); EnableToken(SE_CREATE_TOKEN_NAME); Statistics := nil; User := nil; Priviledges := nil; Owner := nil; PrimaryGroup := nil; DefaultDacl := nil; TokenSrc := nil; Groups := nil; TokenNewGroups := nil; try Statistics := GetFromToken(SystemToken, TokenStatistics); User := GetFromToken(SystemToken, TokenUser); Priviledges := GetFromToken(SystemToken, TokenPrivileges); Owner := GetFromToken(SystemToken, TokenOwner); PrimaryGroup := GetFromToken(SystemToken, TokenPrimaryGroup); DefaultDacl := GetFromToken(SystemToken, TokenDefaultDacl); TokenSrc := GetFromToken(SystemToken, TokenSource); Groups := GetFromToken(SystemToken, JwaWinNT.TokenGroups); TokenNewGroups := GetMemory(SizeOf(TokenNewGroups^) + SizeOf(TokenNewGroups^.Groups) * Groups^.GroupCount); TokenNewGroups^.GroupCount := Groups^.GroupCount + 1; for I := 0 to Groups^.GroupCount - 1 do TokenNewGroups^.Groups[I] := Groups^.Groups[I]; TokenNewGroups^.Groups[Groups^.GroupCount].Sid := Pointer(User^.User.Sid); TokenNewGroups^.Groups[Groups^.GroupCount].Attributes := SE_GROUP_MANDATORY or SE_GROUP_ENABLED or SE_GROUP_ENABLED_BY_DEFAULT or SE_GROUP_LOGON_ID; NtCheck( NtCreateToken( @Result^.UserToken, TOKEN_ALL_ACCESS, nil, Statistics^.TokenType, @Statistics^.AuthenticationId, @Statistics^.ExpirationTime, User, TokenNewGroups, Priviledges, Owner, PrimaryGroup, DefaultDacl, TokenSrc )); finally if (Statistics <> nil) then FreeMemory(Statistics); if (User <> nil) then FreeMemory(User); if (Priviledges <> nil) then FreeMemory(Priviledges); if (Owner <> nil) then FreeMemory(Owner); If (PrimaryGroup <> nil) then FreeMemory(PrimaryGroup); if (DefaultDacl <> nil) then FreeMemory(DefaultDacl); if (TokenSrc <> nil) then FreeMemory(TokenSrc); if (Groups <> nil) then FreeMemory(Groups); if (TokenNewGroups <> nil) then FreeMemory(TokenNewGroups); end; try try Profile := AllocEmptyProfile(ProfileLength); except CloseHandle(Result^.UserToken); Raise; end; finally CloseHandle(SystemToken); end; end; |
4) Now we can create a buffer:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | function CreateAutoLogonBuffer32( const UserName, Domain, Password : WideString; WorkingMode : TWorkingMode; const PrimarySID : JwaWindows.PSID; TokenGroups : JWaWindows.PTokenGroups; const Privs : JwaWindows.PTokenPrivileges ) : PAutoLogonBuffer32; var Profile : PMSV1_0_INTERACTIVE_PROFILE; ProfileLength : DWORD; CurrentPosition : Pointer; bDoFreeTokenHandle : Boolean; begin Result := GetMemory(SizeOf(Result^)); try ZeroMemory(Result, SizeOf(Result^)); case WorkingMode of wmNormalLogin : LoginUser; wmCreateToken : CreateToken; wmAsSystem : AsSystem; end; // PrintTokenSIDs(Result^.UserToken); bDoFreeTokenHandle := True; try if Profile^.MessageType <> MsV1_0InteractiveProfile then begin Writeln('Profile message type is not MsV1_0InteractiveProfile, but is ', Ord(Profile^.MessageType)); Abort; end; Result^.SourceProcessId := GetCurrentProcessId; CurrentPosition := @Result^.DynamicData[0]; if Is2003 then begin Result^.Credentials.WIN2K_MODE.UserName := CopyString(UserName, Result, CurrentPosition); Result^.Credentials.WIN2K_MODE.Domain := CopyString(Domain, Result, CurrentPosition); Result^.Credentials.WIN2K_MODE.DomainLength := Length(Domain); Result^.Credentials.WIN2K_MODE.ProfileLength := ProfileLength; end else begin Result^.Credentials.WINXP_MODE.UserName := CopyString(UserName, Result, CurrentPosition); Result^.Credentials.WINXP_MODE.Domain := CopyString(Domain, Result, CurrentPosition); Result^.Credentials.WINXP_MODE.ProfileLength := ProfileLength; end; Result^.MessageType := Ord(MsV1_0InteractiveProfile); Result^.LogonCount := Profile^.LogonCount; Result^.BadPasswordCount := Profile^.BadPasswordCount; Result^.ProfileLogonTime := Profile^.LogonTime; Result^.LogoffTime := Profile^.LogoffTime; Result^.KickOffTime := Profile^.KickOffTime; Result^.PasswordLastSet := Profile^.PasswordLastSet; Result^.PasswordCanChange := Profile^.PasswordCanChange; Result^.PasswordMustChange := Profile^.PasswordMustChange; CopyUnicodeString(@Result^.LogonScript, @Profile^.LogonScript, Result, CurrentPosition); CopyUnicodeString(@Result^.HomeDirectory, @Profile^.HomeDirectory, Result, CurrentPosition); CopyUnicodeString(@Result^.FullName, @Profile^.FullName, Result, CurrentPosition); CopyUnicodeString(@Result^.ProfilePath, @Profile^.ProfilePath, Result, CurrentPosition); CopyUnicodeString(@Result^.HomeDirectoryDrive, @Profile^.HomeDirectoryDrive, Result, CurrentPosition); CopyUnicodeString(@Result^.LogonServer, @Profile^.LogonServer, Result, CurrentPosition); Result^.UserFlags := Profile^.UserFlags; Result^.LogonTime := GetCurrentTime(); Result^.SmartCardLogon := False; Result^.TotalLength.Length := Cardinal(CurrentPosition) - Cardinal(Result); bDoFreeTokenHandle := False; finally if (bDoFreeTokenHandle) then CloseHandle(Result^.UserToken); LsaFreeReturnBuffer(Profile); end; except FreeMemory(Result); Raise; end; end; |
5) In case of x64 systems we need to copy the whole structure into 64 bit structure:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 | type PWSTR_64 = record Buffer : PWSTR; Alignment : DWORD; end; THANDLE_64 = record Handle : THandle; Alignment : DWORD; end; PUNICODE_STRING_64 = ^UNICODE_STRING_64; UNICODE_STRING_64 = record Length: USHORT; MaximumLength: USHORT; Alignment : DWORD; Buffer: PWSTR_64; end; ULONG_64 = record Value : DWORD; Alignment : DWORD; end; QUOTA_LIMITS_64 = record PagedPoolLimit: ULONG_64; NonPagedPoolLimit: ULONG_64; MinimumWorkingSetSize: ULONG_64; MaximumWorkingSetSize: ULONG_64; PagefileLimit: ULONG_64; TimeLimit: LARGE_INTEGER; end; PAutoLogonBuffer64 = ^TAutoLogonBuffer64; TAutoLogonBuffer64 = record TotalLength : TLengthBuffer; SourceProcessId : DWORD; UserToken : THANDLE_64; LogonId : LUID; Quotas : QUOTA_LIMITS_64; Credentials : record case Boolean of True : (WINXP_MODE : record UserName : PWSTR_64; Domain : PWSTR_64; ProfileLength : ULONG; Unknown1 : DWORD; end); False : (WIN2K_MODE : record UserName : PWSTR_64; DomainLength : ULONG_64; Domain : PWSTR_64; ProfileLength : ULONG_64; end); end; MessageType : DWORD; LogonCount : USHORT; BadPasswordCount: USHORT; ProfileLogonTime: LARGE_INTEGER; LogoffTime : LARGE_INTEGER; KickOffTime : LARGE_INTEGER; PasswordLastSet : LARGE_INTEGER; PasswordCanChange: LARGE_INTEGER; PasswordMustChange: LARGE_INTEGER; LogonScript : UNICODE_STRING_64; HomeDirectory : UNICODE_STRING_64; FullName : UNICODE_STRING_64; ProfilePath : UNICODE_STRING_64; HomeDirectoryDrive : UNICODE_STRING_64; LogonServer : UNICODE_STRING_64; UserFlags : ULONG_64; LogonTime : LARGE_INTEGER; SmartCardLogon : BOOL; PrivateDataLen : ULONG; PrivateData : Pointer; Unknown2 : DWORD; DynamicData : array [0..7981] of BYTE; end; function CreateAutoLogonBuffer64(const Source : PAutoLogonBuffer32) : PAutoLogonBuffer64; var DynamicBufferDelta : Integer; begin Result := GetMemory(SizeOf(Result^)); try ZeroMemory(Result, SizeOf(Result^)); DynamicBufferDelta := ((Cardinal(@Result^.DynamicData) - Cardinal(Result) - (Cardinal(@Source^.DynamicData) - Cardinal(Source)))); Result^.TotalLength.Length := Source^.TotalLength.Length + Cardinal(DynamicBufferDelta); Result^.SourceProcessId := Source^.SourceProcessId; Result^.UserToken.Handle := Source^.UserToken; Result^.LogonId := Source^.LogonId; Result^.Quotas.PagedPoolLimit.Value := Source^.Quotas.PagedPoolLimit; Result^.Quotas.NonPagedPoolLimit.Value := Source^.Quotas.NonPagedPoolLimit; Result^.Quotas.MinimumWorkingSetSize.Value := Source^.Quotas.MinimumWorkingSetSize; Result^.Quotas.MaximumWorkingSetSize.Value := Source^.Quotas.MaximumWorkingSetSize; Result^.Quotas.PagefileLimit.Value := Source^.Quotas.PagefileLimit; Result^.Quotas.TimeLimit := Source^.Quotas.TimeLimit; // In theory we need to do a check if it's windows 2003/xp x64, but actually there is no public version of XP (5.1) x64, so // we can just skip it. Result^.Credentials.WIN2K_MODE.UserName.Buffer := AdjustBuffer(Source^.Credentials.WIN2K_MODE.UserName, DynamicBufferDelta); Result^.Credentials.WIN2K_MODE.DomainLength.Value := Source^.Credentials.WIN2K_MODE.DomainLength; Result^.Credentials.WIN2K_MODE.Domain.Buffer := AdjustBuffer(Source^.Credentials.WIN2K_MODE.Domain, DynamicBufferDelta); Result^.Credentials.WIN2K_MODE.ProfileLength.Value:= Source^.Credentials.WIN2K_MODE.ProfileLength; Result^.MessageType := Source^.MessageType; Result^.LogonCount := Source^.LogonCount; Result^.BadPasswordCount := Source^.BadPasswordCount; Result^.ProfileLogonTime := Source^.ProfileLogonTime; Result^.LogoffTime := Source^.LogoffTime; Result^.KickOffTime := Source^.KickOffTime; Result^.PasswordLastSet := Source^.PasswordLastSet; Result^.PasswordCanChange := Source^.PasswordCanChange; Result^.PasswordMustChange := Source^.PasswordMustChange; Result^.LogonScript := AdjustUnicodeString(Source^.LogonScript, DynamicBufferDelta); Result^.HomeDirectory := AdjustUnicodeString(Source^.HomeDirectory, DynamicBufferDelta); Result^.FullName := AdjustUnicodeString(Source^.FullName, DynamicBufferDelta); Result^.ProfilePath := AdjustUnicodeString(Source^.ProfilePath, DynamicBufferDelta); Result^.HomeDirectoryDrive:= AdjustUnicodeString(Source^.HomeDirectoryDrive, DynamicBufferDelta); Result^.LogonServer := AdjustUnicodeString(Source^.LogonServer, DynamicBufferDelta); Result^.UserFlags.Value := Source^.UserFlags; Result^.LogonTime := Source^.LogonTime; Result^.SmartCardLogon := Source^.SmartCardLogon; Result^.PrivateDataLen := Source^.PrivateDataLen; Result^.PrivateData := AdjustBuffer(Source^.PrivateData, DynamicBufferDelta); Result^.Unknown2 := Source^.Unknown2; CopyMemory(@Result^.DynamicData, @Source^.DynamicData, Source^.TotalLength.Length - (SizeOf(Source^) - SizeOf(Source^.DynamicData))); except FreeMemory(Result); Raise; end; end; function CreateAutoLogonBuffer( const UserName, Domain, Password : WideString; WorkingMode : TWorkingMode; const PrimarySID : JwaWindows.PSID; TokenGroups : JWaWindows.PTokenGroups; const Privs : JwaWindows.PTokenPrivileges ) : PLengthBuffer; begin Result := @CreateAutoLogonBuffer32(UserName, Domain, Password, WorkingMode, PrimarySID, TokenGroups, Privs)^.TotalLength; if (IsWow64) then try Result := @CreateAutoLogonBuffer64(PAutoLogonBuffer32(Result))^.TotalLength; except FreeMemory(Result); Raise; end; end; procedure FreeAutoLogonBuffer(var Buffer : PLengthBuffer); begin if (Buffer <> nil) then begin if (IsWow64) then CloseHandle(PAutoLogonBuffer64(Buffer)^.UserToken.Handle) else CloseHandle(PAutoLogonBuffer32(Buffer)^.UserToken); FreeMemory(Buffer); Buffer := nil; end; end; |
6) Now we need to open the pipe, send the WLX_SAS_TYPE_AUTHENTICATED SAS to Winlogon and write the data to the pipe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function SendCredentialThreadProc(Parameter: PLengthBuffer): Integer; var PipeHandle : THandle; begin Result := 0; try PipeHandle := OpenPipe; try DoLogonNotifyMessage(WPARAM_AUTHENTICATED, 0); ProcessPipe(PipeHandle, Parameter); finally CloseHandle(PipeHandle); end; except on E : EOSError do Result := EOsError(E).ErrorCode; on E : Exception do Result := ERROR_GEN_FAILURE; end; EndThread(Result); end; |
7) So, the main procedure will look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | procedure DoLogon(const UserName, Domain, Password : WideString; WorkingMode : TWorkingMode; const PrimarySIDStr : String; const AdditionalSIDS : TStringDynArray; const PriviledgesList : TStringDynArray; Timeout : Cardinal); var Buffer : PLengthBuffer; TokenGroups : JWAWindows.PTokenGroups; PrimarySID : JWaWindows.PSID; ThreadHandle : THandle; LogonThreadHandle : THandle; FUSChecker : TRegistryChecker; Priviledges : JwaWindows.PTokenPrivileges; begin CheckLoggedOnUser; CheckScreenSaver; TryToConnectToPipe; FUSChecker := TRegistryChecker.Create(FUSKey, False); try PrimarySID := CreateSID(PrimarySIDStr); try TokenGroups := GetAdditionalSIDS(AdditionalSIDS); try Priviledges := CreatePriviledges(PriviledgesList); try Buffer := CreateAutoLogonBuffer(UserName, Domain, Password, WorkingMode, PrimarySID, TokenGroups, Priviledges); try LogonThreadHandle := WaitForLogonAsync; try ThreadHandle := SendCredentialsAsync(Buffer); try WaitForThreads(ThreadHandle, LogonThreadHandle, Timeout); finally CloseHandle(ThreadHandle); end; finally CloseHandle(LogonThreadHandle); end; finally FreeAutoLogonBuffer(Buffer); end; finally FreePriviledges(Priviledges); end; finally FreeAdditionalSIDs(TokenGroups); end finally _FreeSID(PrimarySID); end; finally FreeAndNil(FUSChecker); end; end; |
AutoLogonXP 1.0 (5569 downloads)
Please note that this sample is free for non-commercial use only. If you require to use it, or it’s source code in your business projects, please send a request using contact form.
One Response for "Autologon user on Windows XP/2003 using AutoReconnect pipe – part 3 (implementation details)"
your code sample is cool.i was unaware of pipe connection with winlogon. actualy i am trying to bypass msoobe.exe so that i can use windows xp mode beyond april 2012 and do my j2me development on linux using wtk3.
cab you help me how can i start a login session with explorer.exe so that i utilise it without activation in virtual pc. my cofigs are –
linux mint 12, vrtualbox, windows xp mode vhd. idont wana use xp for work but just j2me development using wtk3 as its not avail on linux.
Leave a reply