<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"
xmlns:content="http://purl.org/rss/1.0/modules/content/"
xmlns:wfw="http://wellformedweb.org/CommentAPI/"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
><channel><title>Remko Weijnen&#039;s Blog (Remko&#039;s Blog) &#187; Terminal Server</title> <atom:link href="http://www.remkoweijnen.nl/blog/topics/terminalserver/feed/" rel="self" type="application/rss+xml" /><link>http://www.remkoweijnen.nl/blog</link> <description>About Terminal Server, Citrix, Delphi and other stuff</description> <lastBuildDate>Tue, 31 Jan 2012 15:37:53 +0000</lastBuildDate> <language>en</language> <sy:updatePeriod>hourly</sy:updatePeriod> <sy:updateFrequency>1</sy:updateFrequency> <generator>http://wordpress.org/?v=3.2.1</generator> <item><title>NTVDM encountered a hard error</title><link>http://www.remkoweijnen.nl/blog/2011/12/14/ntvdm-encountered-a-hard-error/</link> <comments>http://www.remkoweijnen.nl/blog/2011/12/14/ntvdm-encountered-a-hard-error/#comments</comments> <pubDate>Wed, 14 Dec 2011 20:41:22 +0000</pubDate> <dc:creator>Remko</dc:creator> <category><![CDATA[C++]]></category> <category><![CDATA[Citrix]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows 2003]]></category> <category><![CDATA[DOS]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=2254</guid> <description><![CDATA[Today I troubleshooted an old DOS application that needed to run on a 32 bit Citrix XenApp Server. The last time I saw an actual DOS application in a production environment must be years ago. When starting the application, the WOW subsystem (NTVDM) crashed with the message: &#8220;NTVM encountered a hard error.&#8221;: After spending some [...]]]></description> <content:encoded><![CDATA[<p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/12/image7.png" rel="lightbox" class="thickbox no_icon" title="MS-Dos Logo"><img style="margin: 0px 3px 0px 0px; display: inline; float: left" title="MS-Dos Logo" alt="MS-Dos Logo" align="left" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/12/image_thumb7.png" width="74" height="82" /></a>Today I troubleshooted an old DOS application that needed to run on a 32 bit Citrix XenApp Server. The last time I saw an actual DOS application in a production environment must be years ago.</p><p>When starting the application, the WOW subsystem (NTVDM) crashed with the message: &#8220;NTVM encountered a hard error.&#8221;:</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/12/image8.png" rel="lightbox" class="thickbox no_icon" title="ntvdm.exe - System Error"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ntvdm.exe - System Error" border="0" alt="NTVDM encoutered a hard error" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/12/image_thumb8.png" width="198" height="104" /></a></p><p>After spending some time troubleshooting I remembered a similar issue from a few years ago where a DOS application worked fine from the Console but refused to work from an RDP or ICA session.</p><p><span id="more-2254"></span><p>And indeed the application works perfectly when run from the Console but not from a Console <u>session</u>. I noticed that the application switched to full screen mode after it was launched (even when I set it to Windowed mode) and presumably this is why ntvdm errors: full-screen mode is disallowed for DOS apps in RDP (and ICA) sessions as documented in <a  href="http://support.microsoft.com/?kbid=192190" target="_blank">Q192190</a>.</p><p>I looked for a way to force the application to run in windowed mode but I was unable to find such a solution. So I decided to test the application in <a  href="http://sourceforge.net/projects/dosbox/" target="_blank">DOSBox</a>, an x86 PC emulator.</p><p>And that worked perfectly, no changes were needed at all to make the application run.</p><p>As an added bonus, DOSBox takes care of typical issues with DOS applications running on Citrix XenApp such as <a  href="http://support.citrix.com/article/CTX846521" target="_blank">keyboard polling and 100% cpu usage</a>.</p><p>I was even more impressed that the application <strong>runs fine with DOSBox on my Windows 7 64 bit machine</strong>!</p><p>There is one thing I didn&#8217;t like though, DOSBox always shows a Splashscreen that fades in and out:</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/12/image9.png" rel="lightbox" class="thickbox no_icon" title="image"><img style="background-image: none; border-right-width: 0px; margin: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/12/image_thumb9.png" width="419" height="277" /></a></p><p>This is typically something that is not desirable on a XenApp (or RDS) environment because it causes many unnecessary screen updates. This may be a non issue on a fast LAN but on a slower WAN or high latency connection it may matter. Do how do we get rid of it?</p><p>There is no commandline argument or config setting that disables the splash so I figured that my only option would be to compile the DOSBox source and leave out the splash screen.</p><p>So I downloaded the source files from the sourceforge project page and launched Visual Studio 2010.</p><p>The Splashscreen is in sdlmain.cpp but I noticed this comment:</p><div class="dean_ch" style="white-space: wrap;"><span class="coMULTI">/* Please leave the Splash screen stuff in working order<br /> &nbsp; &nbsp;in DOSBox. We spend a lot of time making DOSBox. */</span></div><p>This presented me with a dilemma: I really think the creators deserve their credit but at the same time I want to get rid of the splash.</p><p>So I decided to change the code in a way that the Splash screen is shown when run from the Console but not when run from an RDP or ICA session. This change was very easy, I surrounded the Splash screen code with a conditional statement:</p><div class="dean_ch" style="white-space: wrap;"><span class="coMULTI">/* We will ignore the splashscreen when in a remote session */</span><br /> &nbsp; &nbsp;<br /> <span class="kw1">if</span> <span class="br0">&#40;</span>!GetSystemMetrics<span class="br0">&#40;</span>SM_REMOTESESSION<span class="br0">&#41;</span><span class="br0">&#41;</span><br /> <span class="br0">&#123;</span><br /> &nbsp; &nbsp; <span class="co1">// Splash screen code</span><br /> <span class="br0">&#125;</span></div><p>In order to compile the code with Visual Studio I followed the <a  href="http://www.dosbox.com/wiki/Building_DOSBox_with_Visual_C_2008_Express" target="_blank">Building DOSBox with Visual C 2008 Express</a> article from the Wiki.</p><p>Below you will find two downloads:</p><ul><li>A binary package, containing my compiled DOSBox.exe and it&#8217;s dependancies.</li><li>A source code package, containing the modified source and the SDL Development Libraries. The modified source is licensed under the <a  href="http://www.gnu.org/copyleft/gpl.html" target="_blank">GNU GPL license</a>.</li></ul><p>If you are going to use DOSBox I highly encourage you to make a donation to <a  href="http://sourceforge.net/donate/index.php?group_id=52551" target="_blank">Support the DOSBox project</a>.</p><p>&#160;</p><p>A few other causes for the &#8220;NTVM encountered a hard error&#8221; message may be:</p><ul><li><font color="#35383d">The %TEMP% or %TMP% variable point to a directory that is not in a short (8.3) format. See also <a  href="http://support.citrix.com/article/CTX110996" target="_blank">CTX110996</a>.</font></li><li>Error message when you run a 16-bit program in Windows Server 2003: &quot;NTVDM has encountered a hard error<font color="#35383d">&#8221; (<a  href="http://support.microsoft.com/kb/937932" target="_blank">Hotfix KB937932</a>).</font></li><li><font color="#35383d">Check that HKLM\System\CurrentControlSet\Control\WOW\DisallowedPolicyDefault is set to 0</font></li><li><font color="#35383d">Check that HKLM\SYSTEM\CurrentControlSet\Control\VirtualDeviceDrivers\VDD doesn&#8217;t contain non existing files.</font></li></ul><ul><a  class="downloadlink" href="http://www.remkoweijnen.nl/blog/download/DOSBox-0.74-Bin.zip" title="Version0.74 downloaded 23 times">DOSBox 0.74 (23)</a></ul><ul><a  class="downloadlink" href="http://www.remkoweijnen.nl/blog/download/DOSBox-0.74-Src.zip" title="Version0.74 downloaded 8 times">DOSBox 0.74 Source Code (8)</a></ul> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/12/14/ntvdm-encountered-a-hard-error/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>SSL Certificates in termsrv.dll</title><link>http://www.remkoweijnen.nl/blog/2011/05/02/ssl-certificates-in-termsrv-dll/</link> <comments>http://www.remkoweijnen.nl/blog/2011/05/02/ssl-certificates-in-termsrv-dll/#comments</comments> <pubDate>Mon, 02 May 2011 12:54:39 +0000</pubDate> <dc:creator>Remko</dc:creator> <category><![CDATA[PowerShell]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows 2008]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/2011/05/02/ssl-certificates-in-termsrv-dll/</guid> <description><![CDATA[I was digging around in termsrv.dll yesterday when I noticed that there are some (well 372 to be exact) SSL certificates inside the Terminal Server binary (termsrv.dll): Two of them seem to actually contain the private keys as well, but I am not 100% sure it may be just a certificate in another format. &#160; [...]]]></description> <content:encoded><![CDATA[<p>I was digging around in termsrv.dll yesterday when I noticed that there are some (well 372 to be exact) SSL certificates inside the Terminal Server binary (termsrv.dll):</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/05/image7.png" rel="lightbox" class="thickbox no_icon" title="image"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/05/image_thumb7.png" width="416" height="195" /></a></p><p>Two of them seem to actually contain the private keys as well, but I am not 100% sure it may be just a certificate in another format.</p><p>&#160;</p><p><span id="more-1726"></span><p>I wrote a small PowerShell script to import these certificates into the personal certificate store so I could inspect them easily:</p><div class="dean_ch" style="white-space: wrap;"><span class="re4"><span class="br0">&#91;</span>void<span class="br0">&#93;</span><span class="br0">&#91;</span><span class="kw3">System</span>.<span class="me1">Reflection</span>.<span class="me1">Assembly</span><span class="br0">&#93;</span></span>::<span class="me2">LoadWithPartialName</span><span class="br0">&#40;</span><span class="st0">&quot;System.Security&quot;</span><span class="br0">&#41;</span></p><p><span class="re3">$store</span> = <span class="re0">New-<span class="re1">Object</span></span> <span class="kw3">System</span>.<span class="me1">Security</span>.<span class="me1">Cryptography</span>.<span class="me1">X509Certificates</span>.<span class="me1">X509Store</span><br /> <span class="re3">$store</span>.<span class="me1">Open</span><span class="br0">&#40;</span><span class="re4"><span class="br0">&#91;</span><span class="kw3">System</span>.<span class="me1">Security</span>.<span class="me1">Cryptography</span>.<span class="me1">X509Certificates</span>.<span class="me1">OpenFlags</span><span class="br0">&#93;</span></span>::<span class="me2">ReadWrite</span><span class="br0">&#41;</span></p><p><span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re3">$file</span> <span class="kw1">in</span> <span class="kw4">dir</span> <span class="re2">-Path</span> c:\Tools\dumper\*.<span class="me1">txt</span><span class="br0">&#41;</span><br /> <span class="br0">&#123;</span><br /> &nbsp; &nbsp;<span class="re3">$cert</span> = <span class="re0">New-<span class="re1">Object</span></span> <span class="kw3">System</span>.<span class="me1">Security</span>.<span class="me1">Cryptography</span>.<span class="me1">X509Certificates</span>.<span class="me1">X509Certificate2</span> <span class="re3">$file</span><br /> &nbsp; &nbsp;<span class="kw1">try</span><br /> &nbsp; &nbsp;<span class="br0">&#123;</span><br /> &nbsp; &nbsp;&nbsp; &nbsp;<span class="re3">$store</span>.<span class="me1">Add</span><span class="br0">&#40;</span><span class="re3">$cert</span><span class="br0">&#41;</span>&nbsp;&nbsp; &nbsp;<br /> &nbsp; &nbsp;<span class="br0">&#125;</span><br /> &nbsp; &nbsp;<span class="kw1">catch</span><br /> &nbsp; &nbsp;<span class="br0">&#123;</span><br /> &nbsp; &nbsp;&nbsp; &nbsp;<span class="re0">Write-<span class="re1">Error</span></span> <span class="br0">&#40;</span><span class="st0">&quot;Error Adding $certfile&quot;</span><span class="br0">&#41;</span> <span class="re2">-ErrorAction</span>:<span class="kw1">Continue</span><br /> &nbsp; &nbsp;<span class="br0">&#125;</span></p><p><span class="br0">&#125;</span></p><p><span class="re3">$store</span>.<span class="me1">close</span></div><p>The certificates appear to be public certificates of root CA&#8217;s, I have no idea of their intended purpose (why they need to be inside the termsrv.dll):</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/05/image8.png" rel="lightbox" class="thickbox no_icon" title="image"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/05/image_thumb8.png" width="408" height="400" /></a></p><p>The keys that were marked as private are these two:</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/05/image9.png" rel="lightbox" class="thickbox no_icon" title="image"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/05/image_thumb9.png" width="411" height="53" /></a></p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/05/02/ssl-certificates-in-termsrv-dll/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Autologon user on Windows XP/2003 using AutoReconnect pipe &#8211; part 3 (implementation details)</title><link>http://www.remkoweijnen.nl/blog/2011/03/03/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-3-implementation-details/</link> <comments>http://www.remkoweijnen.nl/blog/2011/03/03/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-3-implementation-details/#comments</comments> <pubDate>Thu, 03 Mar 2011 12:00:46 +0000</pubDate> <dc:creator>daNIL</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows 2003]]></category> <category><![CDATA[Windows Internals]]></category> <category><![CDATA[Windows XP]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1396</guid> <description><![CDATA[In the previous parts (part 1 part 2) i&#8217;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: function CopyString&#40;const Source : WideString; const Buffer : Pointer; var CurrentPosition : Pointer; LengthInBytes: Cardinal = $FFFFFFFF&#41; [...]]]></description> <content:encoded><![CDATA[<p>In the previous parts (<a  href="http://www.remkoweijnen.nl/blog/2011/02/09/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-1-theory/">part 1</a> <a  href="http://www.remkoweijnen.nl/blog/2011/03/02/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-2-problems-and-workarounds/">part 2</a>) i&#8217;ve described the theoretical part and implementation problems. So, now we can write the code:</p><p>1) In case we login the user, we just call <a  href="http://msdn.microsoft.com/en-us/library/aa378292(v=vs.85).aspx">LsaLogonUser</a> to get the token:<br /> <span id="more-1396"></span></p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> CopyString<span class="br0">&#40;</span><span class="kw1">const</span> Source : <span class="kw4">WideString</span>; <span class="kw1">const</span> Buffer : <span class="kw4">Pointer</span>; <span class="kw1">var</span> CurrentPosition : <span class="kw4">Pointer</span>; LengthInBytes: <span class="kw4">Cardinal</span> = <span class="re0">$FFFFFFFF</span><span class="br0">&#41;</span> : <span class="kw4">Pointer</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Source &lt;&gt; <span class="st0">&#8221;</span> <span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span> := <span class="kw4">Cardinal</span><span class="br0">&#40;</span>CurrentPosition<span class="br0">&#41;</span> &#8211; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>LengthInBytes = <span class="re0">$FFFFFFFF</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; LengthInBytes := <span class="br0">&#40;</span><span class="kw3">Length</span><span class="br0">&#40;</span>Source<span class="br0">&#41;</span> + <span class="nu0">1</span><span class="br0">&#41;</span> * <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Source<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; CopyMemory<span class="br0">&#40;</span>CurrentPosition, <span class="kw4">PWideChar</span><span class="br0">&#40;</span>Source<span class="br0">&#41;</span>, LengthInBytes<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>CurrentPosition<span class="br0">&#41;</span> := <span class="kw4">Cardinal</span><span class="br0">&#40;</span>CurrentPosition<span class="br0">&#41;</span> + LengthInBytes;<br /> &nbsp; <span class="kw1">end</span><br /> &nbsp; <span class="kw1">else</span><br /> &nbsp; &nbsp; Result := <span class="kw2">nil</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> CopyUnicodeString<span class="br0">&#40;</span><span class="kw1">const</span> Target : PUnicodeString; <span class="kw1">const</span> Source : PUnicodeString; <span class="kw1">const</span> Buffer : <span class="kw4">Pointer</span>; <span class="kw1">var</span> CurrentPosition : <span class="kw4">Pointer</span><span class="br0">&#41;</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Target^ := Source^;<br /> &nbsp; Target^.<span class="me1">Buffer</span> := CopyString<span class="br0">&#40;</span>Target^.<span class="me1">Buffer</span>, Buffer, CurrentPosition, Target^.<span class="me1">MaximumLength</span><span class="br0">&#41;</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> CreateLsaLogonPackageBuffer<span class="br0">&#40;</span><span class="kw1">const</span> UserName, Domain, Password : <span class="kw4">WideString</span>;<br /> &nbsp; out Result : PMSV1_0_INTERACTIVE_LOGON; out ResultLength : <span class="kw4">Cardinal</span><span class="br0">&#41;</span>;<br /> <span class="kw1">var</span><br /> &nbsp; LogonCurrentPointer : <span class="kw4">Pointer</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; ResultLength := <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span> + <span class="br0">&#40;</span><span class="kw3">Length</span><span class="br0">&#40;</span>UserName<span class="br0">&#41;</span> + <span class="kw3">Length</span><span class="br0">&#40;</span>Domain<span class="br0">&#41;</span> + <span class="kw3">Length</span><span class="br0">&#40;</span>Password<span class="br0">&#41;</span> + <span class="nu0">3</span><span class="br0">&#41;</span> * <span class="kw3">SizeOf</span><span class="br0">&#40;</span>UserName<span class="br0">&#91;</span><span class="nu0">1</span><span class="br0">&#93;</span><span class="br0">&#41;</span>;<br /> &nbsp; Result := GetMemory<span class="br0">&#40;</span>ResultLength<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; ZeroMemory<span class="br0">&#40;</span>Result, ResultLength<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>LogonCurrentPointer<span class="br0">&#41;</span> := <span class="kw4">Cardinal</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span> + <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">MessageType</span> := MsV1_0InteractiveLogon;</p><p>&nbsp; &nbsp; RtlInitUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">UserName</span>, <span class="kw4">PWideChar</span><span class="br0">&#40;</span>UserName<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">UserName</span>, @Result^.<span class="me1">UserName</span>, <span class="kw2">nil</span>, LogonCurrentPointer<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; RtlInitUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">LogonDomainName</span>, <span class="kw4">PWideChar</span><span class="br0">&#40;</span>Domain<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">LogonDomainName</span>, @Result^.<span class="me1">LogonDomainName</span>, <span class="kw2">nil</span>, LogonCurrentPointer<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; RtlInitUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">Password</span>, <span class="kw4">PWideChar</span><span class="br0">&#40;</span>Password<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">Password</span>, @Result^.<span class="me1">Password</span>, <span class="kw2">nil</span>, LogonCurrentPointer<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> LoginUser;<br /> <span class="kw1">var</span><br /> &nbsp; LsaHandle : <span class="kw4">THandle</span>;<br /> &nbsp; LsaName : LSA_STRING;<br /> &nbsp; PackageName : LSA_STRING;<br /> &nbsp; AuthenticationPackage : ULONG;<br /> &nbsp; LogonPackageBuffer : PMSV1_0_INTERACTIVE_LOGON ;<br /> &nbsp; LogonPackageBufferLength : <span class="kw4">Cardinal</span>;<br /> &nbsp; TokenSource : JwaWindows.<span class="me1">TTokenSource</span>;<br /> &nbsp; SubStatus : <span class="kw4">Integer</span>;<br /> &nbsp; SecurityMode : LSA_OPERATIONAL_MODE;<br /> <span class="kw1">begin</span><br /> &nbsp; RtlInitUnicodeString<span class="br0">&#40;</span>@LsaName, <span class="st0">&#8216;AutoLogonXP login&#8217;</span><span class="br0">&#41;</span>;<br /> &nbsp; NtCheck<span class="br0">&#40;</span>LsaRegisterLogonProcess<span class="br0">&#40;</span>LsaName, LsaHandle, @SecurityMode<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; RtlInitString<span class="br0">&#40;</span>@PackageName, PCSZ<span class="br0">&#40;</span><span class="kw4">PChar</span><span class="br0">&#40;</span>MSV1_0_PACKAGE_NAME<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; LsaLookupAuthenticationPackage<span class="br0">&#40;</span>LsaHandle, PackageName, AuthenticationPackage<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; JwaWindows.<span class="me1">AllocateLocallyUniqueId</span><span class="br0">&#40;</span>TokenSource.<span class="me1">SourceIdentifier</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; TokenSource.<span class="me1">SourceName</span> := <span class="st0">&#8216;AUTOLGON&#8217;</span>;</p><p>&nbsp; &nbsp; CreateLsaLogonPackageBuffer<span class="br0">&#40;</span>UserName, Domain, Password, LogonPackageBuffer, LogonPackageBufferLength<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; NtCheck<span class="br0">&#40;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; LsaLogonUser<span class="br0">&#40;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LsaHandle,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LsaName,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Interactive,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; AuthenticationPackage,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LogonPackageBuffer,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LogonPackageBufferLength,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; TokenGroups,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @TokenSource,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw4">Pointer</span><span class="br0">&#40;</span>Profile<span class="br0">&#41;</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProfileLength,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; JwaWindows.<span class="me1">LUID</span><span class="br0">&#40;</span>Result^.<span class="me1">LogonId</span><span class="br0">&#41;</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">UserToken</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; JwaWindows.<span class="me1">QUOTA_LIMITS</span><span class="br0">&#40;</span>Result^.<span class="me1">Quotas</span><span class="br0">&#41;</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SubStatus<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; FreeLsaLogonPackageBuffer<span class="br0">&#40;</span>LogonPackageBuffer<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; LsaDeregisterLogonProcess<span class="br0">&#40;</span>LsaHandle<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>2) In case we need to create a token, we call <a  href="http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Token/NtCreateToken.html">NtCreateToken</a>. If there are no groups, we create a single group with the Primary SID in this group :</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">procedure</span> CreateToken;<br /> <span class="kw1">var</span><br /> &nbsp; ExpirationTime : LARGE_INTEGER;<br /> &nbsp; User : JwaWindows.<span class="me1">TOKEN_USER</span>;<br /> &nbsp; TokenSource : TOKEN_SOURCE;<br /> &nbsp; PrimaryGroup : JwaWindows.<span class="me1">TOKEN_PRIMARY_GROUP</span>;<br /> &nbsp; EmptyTokenGroups : JwaWindows.<span class="me1">TTokenGroups</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; ExpirationTime.<span class="me1">QuadPart</span> := <span class="nu0">0</span>;</p><p>&nbsp; User.<span class="me1">User</span>.<span class="me1">Sid</span> := PrimarySID;<br /> &nbsp; User.<span class="me1">User</span>.<span class="me1">Attributes</span> := <span class="nu0">0</span>;</p><p>&nbsp; TokenSource.<span class="me1">SourceName</span> := <span class="st0">&#8216;AUTOLGON&#8217;</span>;<br /> &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>AllocateLocallyUniqueId<span class="br0">&#40;</span>TokenSource.<span class="me1">SourceIdentifier</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; Result^.<span class="me1">LogonId</span> := SYSTEM_LUID;</p><p>&nbsp; PrimaryGroup.<span class="me1">PrimaryGroup</span> := PrimarySID;</p><p>&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw2">nil</span> = TokenGroups<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; EmptyTokenGroups.<span class="me1">GroupCount</span> := <span class="nu0">1</span>;</p><p>&nbsp; &nbsp; EmptyTokenGroups.<span class="me1">Groups</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="me1">Sid</span> := PrimarySID;<br /> &nbsp; &nbsp; EmptyTokenGroups.<span class="me1">Groups</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>.<span class="me1">Attributes</span> := SE_GROUP_MANDATORY <span class="kw1">or</span> SE_GROUP_ENABLED <span class="kw1">or</span> SE_GROUP_ENABLED_BY_DEFAULT <span class="kw1">or</span> SE_GROUP_LOGON_ID;</p><p>&nbsp; &nbsp; TokenGroups := @EmptyTokenGroups;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; EnableToken<span class="br0">&#40;</span>SE_CREATE_TOKEN_NAME<span class="br0">&#41;</span>;<br /> &nbsp; NtCheck<span class="br0">&#40;</span><br /> &nbsp; &nbsp; NtCreateToken<span class="br0">&#40;</span><br /> &nbsp; &nbsp; &nbsp; @Result^.<span class="me1">UserToken</span>,<br /> &nbsp; &nbsp; &nbsp; TOKEN_ALL_ACCESS,<br /> &nbsp; &nbsp; &nbsp; <span class="kw2">nil</span>,<br /> &nbsp; &nbsp; &nbsp; TokenPrimary,<br /> &nbsp; &nbsp; &nbsp; @Result^.<span class="me1">LogonId</span>,<br /> &nbsp; &nbsp; &nbsp; @ExpirationTime,<br /> &nbsp; &nbsp; &nbsp; @User,<br /> &nbsp; &nbsp; &nbsp; TokenGroups,<br /> &nbsp; &nbsp; &nbsp; Privs,<br /> &nbsp; &nbsp; &nbsp; <span class="kw2">nil</span>,<br /> &nbsp; &nbsp; &nbsp; @PrimaryGroup,<br /> &nbsp; &nbsp; &nbsp; <span class="kw2">nil</span>,<br /> &nbsp; &nbsp; &nbsp; @TokenSource<br /> &nbsp; &nbsp; <span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; Profile := AllocEmptyProfile<span class="br0">&#40;</span>ProfileLength<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>Result^.<span class="me1">UserToken</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p><span class="kw1">end</span>;</div><p>3) In case we want to use a &#8220;current&#8221; system token, we need to do some adjustments with it &#8211; we need to add the group with <em>SE_GROUP_LOGON_ID</em> flag to it:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> GetFromToken<span class="br0">&#40;</span>TokenHandle : <span class="kw4">THandle</span>; Info : TOKEN_INFORMATION_CLASS<span class="br0">&#41;</span> : <span class="kw4">Pointer</span>;<br /> <span class="kw1">var</span><br /> &nbsp; Res : <span class="kw4">DWORD</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="kw2">nil</span>;<br /> &nbsp; GetTokenInformation<span class="br0">&#40;</span>TokenHandle, info, <span class="kw2">nil</span>, <span class="nu0">0</span>, Res<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">if</span> <span class="kw3">GetLastError</span> = ERROR_INSUFFICIENT_BUFFER <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp;Result := GetMemory<span class="br0">&#40;</span>Res<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp;<span class="kw3">Win32Check</span><span class="br0">&#40;</span>GetTokenInformation<span class="br0">&#40;</span>TokenHandle, info, Result, Res, Res<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span> <span class="kw1">else</span><br /> &nbsp; &nbsp; <span class="kw3">RaiseLastOSError</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> AsSystem;<br /> <span class="kw1">var</span><br /> &nbsp; SystemToken : <span class="kw4">THandle</span>;</p><p>&nbsp; Statistics : PTokenStatistics;<br /> &nbsp; User : JwaWinNT.<span class="me1">PTokenUser</span>;<br /> &nbsp; Priviledges : JwaWindows.<span class="me1">PTokenPrivileges</span>;<br /> &nbsp; Owner : PTokenOwner;<br /> &nbsp; Groups : JwaWindows.<span class="me1">PTokenGroups</span>;<br /> &nbsp; PrimaryGroup : PTokenPrimaryGroup;<br /> &nbsp; DefaultDacl : PTokenDefaultDacl;<br /> &nbsp; TokenSrc : PTokenSource;</p><p>&nbsp; TokenNewGroups : JwaWindows.<span class="me1">PTokenGroups</span>;<br /> &nbsp; I : <span class="kw4">Integer</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>OpenProcessToken<span class="br0">&#40;</span>GetCurrentProcess, TOKEN_ALL_ACCESS, SystemToken<span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; EnableToken<span class="br0">&#40;</span>SE_CREATE_TOKEN_NAME<span class="br0">&#41;</span>;</p><p>&nbsp; Statistics := <span class="kw2">nil</span>;<br /> &nbsp; User := <span class="kw2">nil</span>;<br /> &nbsp; Priviledges := <span class="kw2">nil</span>;<br /> &nbsp; Owner := <span class="kw2">nil</span>;<br /> &nbsp; PrimaryGroup := <span class="kw2">nil</span>;<br /> &nbsp; DefaultDacl := <span class="kw2">nil</span>;<br /> &nbsp; TokenSrc := <span class="kw2">nil</span>;<br /> &nbsp; Groups := <span class="kw2">nil</span>;<br /> &nbsp; TokenNewGroups := <span class="kw2">nil</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; Statistics := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenStatistics<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; User := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenUser<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Priviledges := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenPrivileges<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Owner := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenOwner<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; PrimaryGroup := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenPrimaryGroup<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; DefaultDacl := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenDefaultDacl<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; TokenSrc := GetFromToken<span class="br0">&#40;</span>SystemToken, TokenSource<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Groups := GetFromToken<span class="br0">&#40;</span>SystemToken, JwaWinNT.<span class="me1">TokenGroups</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; TokenNewGroups := GetMemory<span class="br0">&#40;</span><span class="kw3">SizeOf</span><span class="br0">&#40;</span>TokenNewGroups^<span class="br0">&#41;</span> + <span class="kw3">SizeOf</span><span class="br0">&#40;</span>TokenNewGroups^.<span class="me1">Groups</span><span class="br0">&#41;</span> * Groups^.<span class="me1">GroupCount</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; TokenNewGroups^.<span class="me1">GroupCount</span> := Groups^.<span class="me1">GroupCount</span> + <span class="nu0">1</span>;</p><p>&nbsp; &nbsp; <span class="kw1">for</span> I := <span class="nu0">0</span> <span class="kw1">to</span> Groups^.<span class="me1">GroupCount</span> &#8211; <span class="nu0">1</span> <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; TokenNewGroups^.<span class="me1">Groups</span><span class="br0">&#91;</span>I<span class="br0">&#93;</span> := Groups^.<span class="me1">Groups</span><span class="br0">&#91;</span>I<span class="br0">&#93;</span>;</p><p>&nbsp; &nbsp; TokenNewGroups^.<span class="me1">Groups</span><span class="br0">&#91;</span>Groups^.<span class="me1">GroupCount</span><span class="br0">&#93;</span>.<span class="me1">Sid</span> := <span class="kw4">Pointer</span><span class="br0">&#40;</span>User^.<span class="me1">User</span>.<span class="me1">Sid</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; TokenNewGroups^.<span class="me1">Groups</span><span class="br0">&#91;</span>Groups^.<span class="me1">GroupCount</span><span class="br0">&#93;</span>.<span class="me1">Attributes</span> := SE_GROUP_MANDATORY <span class="kw1">or</span> SE_GROUP_ENABLED <span class="kw1">or</span> SE_GROUP_ENABLED_BY_DEFAULT <span class="kw1">or</span> SE_GROUP_LOGON_ID;</p><p>&nbsp; &nbsp; NtCheck<span class="br0">&#40;</span><br /> &nbsp; &nbsp; &nbsp; NtCreateToken<span class="br0">&#40;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; @Result^.<span class="me1">UserToken</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; TOKEN_ALL_ACCESS,<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">nil</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; Statistics^.<span class="me1">TokenType</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; @Statistics^.<span class="me1">AuthenticationId</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; @Statistics^.<span class="me1">ExpirationTime</span>,<br /> &nbsp; &nbsp; &nbsp; &nbsp; User,<br /> &nbsp; &nbsp; &nbsp; &nbsp; TokenNewGroups,<br /> &nbsp; &nbsp; &nbsp; &nbsp; Priviledges,<br /> &nbsp; &nbsp; &nbsp; &nbsp; Owner,<br /> &nbsp; &nbsp; &nbsp; &nbsp; PrimaryGroup,<br /> &nbsp; &nbsp; &nbsp; &nbsp; DefaultDacl,<br /> &nbsp; &nbsp; &nbsp; &nbsp; TokenSrc<br /> &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Statistics &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Statistics<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>User &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>User<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Priviledges &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Priviledges<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Owner &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Owner<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">If</span> <span class="br0">&#40;</span>PrimaryGroup &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>PrimaryGroup<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>DefaultDacl &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>DefaultDacl<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>TokenSrc &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>TokenSrc<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Groups &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Groups<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>TokenNewGroups &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>TokenNewGroups<span class="br0">&#41;</span>;</p><p>&nbsp; <span class="kw1">end</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; Profile := AllocEmptyProfile<span class="br0">&#40;</span>ProfileLength<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>Result^.<span class="me1">UserToken</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>SystemToken<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>4) Now we can create a buffer:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> CreateAutoLogonBuffer32<span class="br0">&#40;</span><br /> &nbsp; <span class="kw1">const</span> UserName, Domain, Password : <span class="kw4">WideString</span>;<br /> &nbsp; WorkingMode : TWorkingMode;<br /> &nbsp; <span class="kw1">const</span> PrimarySID : JwaWindows.<span class="me1">PSID</span>; &nbsp;TokenGroups : JWaWindows.<span class="me1">PTokenGroups</span>;<br /> &nbsp; <span class="kw1">const</span> Privs : JwaWindows.<span class="me1">PTokenPrivileges</span><br /> &nbsp; <span class="br0">&#41;</span> : PAutoLogonBuffer32;<br /> <span class="kw1">var</span><br /> &nbsp; Profile : PMSV1_0_INTERACTIVE_PROFILE;<br /> &nbsp; ProfileLength : <span class="kw4">DWORD</span>;</p><p>&nbsp; CurrentPosition : <span class="kw4">Pointer</span>;<br /> &nbsp; bDoFreeTokenHandle : <span class="kw4">Boolean</span>;</p><p><span class="kw1">begin</span><br /> &nbsp; Result := GetMemory<span class="br0">&#40;</span><span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; ZeroMemory<span class="br0">&#40;</span>Result, <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">case</span> WorkingMode <span class="kw1">of</span><br /> &nbsp; &nbsp; &nbsp; wmNormalLogin : LoginUser;<br /> &nbsp; &nbsp; &nbsp; wmCreateToken : CreateToken;<br /> &nbsp; &nbsp; &nbsp; wmAsSystem &nbsp; &nbsp;: AsSystem;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; <span class="co1">// &nbsp;PrintTokenSIDs(Result^.UserToken);</span></p><p>&nbsp; &nbsp; bDoFreeTokenHandle := <span class="kw2">True</span>;<br /> &nbsp; &nbsp; <span class="kw1">try</span></p><p>&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> Profile^.<span class="me1">MessageType</span> &lt;&gt; MsV1_0InteractiveProfile <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Writeln</span><span class="br0">&#40;</span><span class="st0">&#8216;Profile message type is not &nbsp;MsV1_0InteractiveProfile, but is &#8216;</span>, <span class="kw3">Ord</span><span class="br0">&#40;</span>Profile^.<span class="me1">MessageType</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Abort</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">SourceProcessId</span> := GetCurrentProcessId;</p><p>&nbsp; &nbsp; &nbsp; CurrentPosition := @Result^.<span class="me1">DynamicData</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span>;</p><p>&nbsp; &nbsp; &nbsp; <span class="kw1">if</span> Is2003 <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">UserName</span> := CopyString<span class="br0">&#40;</span>UserName, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">Domain</span> := CopyString<span class="br0">&#40;</span>Domain, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">DomainLength</span> := <span class="kw3">Length</span><span class="br0">&#40;</span>Domain<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">ProfileLength</span> := ProfileLength;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">end</span> <span class="kw1">else</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WINXP_MODE</span>.<span class="me1">UserName</span> := CopyString<span class="br0">&#40;</span>UserName, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WINXP_MODE</span>.<span class="me1">Domain</span> := CopyString<span class="br0">&#40;</span>Domain, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WINXP_MODE</span>.<span class="me1">ProfileLength</span> := ProfileLength;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">MessageType</span> := <span class="kw3">Ord</span><span class="br0">&#40;</span>MsV1_0InteractiveProfile<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">LogonCount</span> := Profile^.<span class="me1">LogonCount</span>;<br /> &nbsp; &nbsp; &nbsp; Result^.<span class="me1">BadPasswordCount</span> := Profile^.<span class="me1">BadPasswordCount</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">ProfileLogonTime</span> := Profile^.<span class="me1">LogonTime</span>;<br /> &nbsp; &nbsp; &nbsp; Result^.<span class="me1">LogoffTime</span> := Profile^.<span class="me1">LogoffTime</span>;<br /> &nbsp; &nbsp; &nbsp; Result^.<span class="me1">KickOffTime</span> := Profile^.<span class="me1">KickOffTime</span>;<br /> &nbsp; &nbsp; &nbsp; Result^.<span class="me1">PasswordLastSet</span> := Profile^.<span class="me1">PasswordLastSet</span>;<br /> &nbsp; &nbsp; &nbsp; Result^.<span class="me1">PasswordCanChange</span> := Profile^.<span class="me1">PasswordCanChange</span>;<br /> &nbsp; &nbsp; &nbsp; Result^.<span class="me1">PasswordMustChange</span> := Profile^.<span class="me1">PasswordMustChange</span>;</p><p>&nbsp; &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">LogonScript</span>, @Profile^.<span class="me1">LogonScript</span>, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">HomeDirectory</span>, @Profile^.<span class="me1">HomeDirectory</span>, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">FullName</span>, @Profile^.<span class="me1">FullName</span>, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">ProfilePath</span>, @Profile^.<span class="me1">ProfilePath</span>, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">HomeDirectoryDrive</span>, @Profile^.<span class="me1">HomeDirectoryDrive</span>, Result, CurrentPosition<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; CopyUnicodeString<span class="br0">&#40;</span>@Result^.<span class="me1">LogonServer</span>, @Profile^.<span class="me1">LogonServer</span>, Result, CurrentPosition<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">UserFlags</span> := Profile^.<span class="me1">UserFlags</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">LogonTime</span> := GetCurrentTime<span class="br0">&#40;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">SmartCardLogon</span> := <span class="kw2">False</span>;</p><p>&nbsp; &nbsp; &nbsp; Result^.<span class="me1">TotalLength</span>.<span class="kw3">Length</span> := <span class="kw4">Cardinal</span><span class="br0">&#40;</span>CurrentPosition<span class="br0">&#41;</span> &#8211; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; bDoFreeTokenHandle := <span class="kw2">False</span>;<br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>bDoFreeTokenHandle<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>Result^.<span class="me1">UserToken</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; LsaFreeReturnBuffer<span class="br0">&#40;</span>Profile<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>5) In case of x64 systems we need to copy the whole structure into 64 bit structure:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">type</span><br /> &nbsp; PWSTR_64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; Buffer : PWSTR;<br /> &nbsp; &nbsp; Alignment : <span class="kw4">DWORD</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; THANDLE_64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; Handle : <span class="kw4">THandle</span>;<br /> &nbsp; &nbsp; Alignment : <span class="kw4">DWORD</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; PUNICODE_STRING_64 = ^UNICODE_STRING_64;<br /> &nbsp; UNICODE_STRING_64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; <span class="kw3">Length</span>: USHORT;<br /> &nbsp; &nbsp; MaximumLength: USHORT;<br /> &nbsp; &nbsp; Alignment : <span class="kw4">DWORD</span>;<br /> &nbsp; &nbsp; Buffer: PWSTR_64;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; ULONG_64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; Value : <span class="kw4">DWORD</span>;<br /> &nbsp; &nbsp; Alignment : <span class="kw4">DWORD</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; QUOTA_LIMITS_64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; PagedPoolLimit: ULONG_64;<br /> &nbsp; &nbsp; NonPagedPoolLimit: ULONG_64;<br /> &nbsp; &nbsp; MinimumWorkingSetSize: ULONG_64;<br /> &nbsp; &nbsp; MaximumWorkingSetSize: ULONG_64;<br /> &nbsp; &nbsp; PagefileLimit: ULONG_64;<br /> &nbsp; &nbsp; TimeLimit: LARGE_INTEGER;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; PAutoLogonBuffer64 = ^TAutoLogonBuffer64;<br /> &nbsp; TAutoLogonBuffer64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; TotalLength &nbsp; &nbsp; : TLengthBuffer;<br /> &nbsp; &nbsp; SourceProcessId : <span class="kw4">DWORD</span>;<br /> &nbsp; &nbsp; UserToken &nbsp; &nbsp; &nbsp; : THANDLE_64;<br /> &nbsp; &nbsp; LogonId &nbsp; &nbsp; &nbsp; &nbsp; : LUID;</p><p>&nbsp; &nbsp; Quotas &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: QUOTA_LIMITS_64;</p><p>&nbsp; &nbsp; Credentials : <span class="kw1">record</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="kw4">Boolean</span> <span class="kw1">of</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">True</span> : <span class="br0">&#40;</span>WINXP_MODE : <span class="kw1">record</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UserName &nbsp; &nbsp; &nbsp; &nbsp;: PWSTR_64;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Domain &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: PWSTR_64;</p><p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ProfileLength &nbsp; : ULONG;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Unknown1 &nbsp; &nbsp; &nbsp; &nbsp;: <span class="kw4">DWORD</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">end</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw2">False</span> : <span class="br0">&#40;</span>WIN2K_MODE : <span class="kw1">record</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UserName &nbsp; &nbsp; &nbsp; &nbsp;: PWSTR_64;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;DomainLength &nbsp; &nbsp;: ULONG_64;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Domain &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: PWSTR_64;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ProfileLength &nbsp; : ULONG_64;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; MessageType &nbsp; &nbsp; : <span class="kw4">DWORD</span>;<br /> &nbsp; &nbsp; LogonCount &nbsp; &nbsp; &nbsp;: USHORT;<br /> &nbsp; &nbsp; BadPasswordCount: USHORT;</p><p>&nbsp; &nbsp; ProfileLogonTime: LARGE_INTEGER;<br /> &nbsp; &nbsp; LogoffTime &nbsp; &nbsp; &nbsp;: LARGE_INTEGER;<br /> &nbsp; &nbsp; KickOffTime &nbsp; &nbsp; : LARGE_INTEGER;<br /> &nbsp; &nbsp; PasswordLastSet : LARGE_INTEGER;<br /> &nbsp; &nbsp; PasswordCanChange: LARGE_INTEGER;<br /> &nbsp; &nbsp; PasswordMustChange: LARGE_INTEGER;</p><p>&nbsp; &nbsp; LogonScript &nbsp; &nbsp; : UNICODE_STRING_64;<br /> &nbsp; &nbsp; HomeDirectory &nbsp; : UNICODE_STRING_64;<br /> &nbsp; &nbsp; FullName &nbsp; &nbsp; &nbsp; &nbsp;: UNICODE_STRING_64;<br /> &nbsp; &nbsp; ProfilePath &nbsp; &nbsp; : UNICODE_STRING_64;<br /> &nbsp; &nbsp; HomeDirectoryDrive : UNICODE_STRING_64;<br /> &nbsp; &nbsp; LogonServer &nbsp; &nbsp; : UNICODE_STRING_64;</p><p>&nbsp; &nbsp; UserFlags &nbsp; &nbsp; &nbsp; : ULONG_64;<br /> &nbsp; &nbsp; LogonTime &nbsp; &nbsp; &nbsp; : LARGE_INTEGER;<br /> &nbsp; &nbsp; SmartCardLogon &nbsp;: <span class="kw4">BOOL</span>;<br /> &nbsp; &nbsp; PrivateDataLen &nbsp;: ULONG;<br /> &nbsp; &nbsp; PrivateData &nbsp; &nbsp; : <span class="kw4">Pointer</span>;<br /> &nbsp; &nbsp; Unknown2 &nbsp; &nbsp; &nbsp; &nbsp;: <span class="kw4">DWORD</span>;</p><p>&nbsp; &nbsp; DynamicData &nbsp; &nbsp; : <span class="kw1">array</span> <span class="br0">&#91;</span><span class="nu0">0</span>..<span class="nu0">7981</span><span class="br0">&#93;</span> <span class="kw1">of</span> <span class="kw4">BYTE</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p><span class="kw1">function</span> CreateAutoLogonBuffer64<span class="br0">&#40;</span><span class="kw1">const</span> Source : PAutoLogonBuffer32<span class="br0">&#41;</span> : PAutoLogonBuffer64;<br /> <span class="kw1">var</span><br /> &nbsp; DynamicBufferDelta : <span class="kw4">Integer</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := GetMemory<span class="br0">&#40;</span><span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; ZeroMemory<span class="br0">&#40;</span>Result, <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; DynamicBufferDelta := <span class="br0">&#40;</span><span class="br0">&#40;</span><span class="kw4">Cardinal</span><span class="br0">&#40;</span>@Result^.<span class="me1">DynamicData</span><span class="br0">&#41;</span> &#8211; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span> &#8211; <span class="br0">&#40;</span><span class="kw4">Cardinal</span><span class="br0">&#40;</span>@Source^.<span class="me1">DynamicData</span><span class="br0">&#41;</span> &#8211; <span class="kw4">Cardinal</span><span class="br0">&#40;</span>Source<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">TotalLength</span>.<span class="kw3">Length</span> := Source^.<span class="me1">TotalLength</span>.<span class="kw3">Length</span> + <span class="kw4">Cardinal</span><span class="br0">&#40;</span>DynamicBufferDelta<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">SourceProcessId</span> := Source^.<span class="me1">SourceProcessId</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">UserToken</span>.<span class="me1">Handle</span> := Source^.<span class="me1">UserToken</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">LogonId</span> := Source^.<span class="me1">LogonId</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">Quotas</span>.<span class="me1">PagedPoolLimit</span>.<span class="me1">Value</span> &nbsp; &nbsp; &nbsp; &nbsp; := Source^.<span class="me1">Quotas</span>.<span class="me1">PagedPoolLimit</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Quotas</span>.<span class="me1">NonPagedPoolLimit</span>.<span class="me1">Value</span> &nbsp; &nbsp; &nbsp;:= Source^.<span class="me1">Quotas</span>.<span class="me1">NonPagedPoolLimit</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Quotas</span>.<span class="me1">MinimumWorkingSetSize</span>.<span class="me1">Value</span> &nbsp;:= Source^.<span class="me1">Quotas</span>.<span class="me1">MinimumWorkingSetSize</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Quotas</span>.<span class="me1">MaximumWorkingSetSize</span>.<span class="me1">Value</span> &nbsp;:= Source^.<span class="me1">Quotas</span>.<span class="me1">MaximumWorkingSetSize</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Quotas</span>.<span class="me1">PagefileLimit</span>.<span class="me1">Value</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:= Source^.<span class="me1">Quotas</span>.<span class="me1">PagefileLimit</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Quotas</span>.<span class="me1">TimeLimit</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:= Source^.<span class="me1">Quotas</span>.<span class="me1">TimeLimit</span>;</p><p>&nbsp; &nbsp; <span class="co1">// In theory we need to do a check if it&#8217;s windows 2003/xp x64, but actually there is no public version of XP (5.1) x64, so</span><br /> &nbsp; &nbsp; <span class="co1">// we can just skip it.</span><br /> &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">UserName</span>.<span class="me1">Buffer</span> &nbsp; &nbsp;:= AdjustBuffer<span class="br0">&#40;</span>Source^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">UserName</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">DomainLength</span>.<span class="me1">Value</span> := Source^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">DomainLength</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">Domain</span>.<span class="me1">Buffer</span> &nbsp; &nbsp; &nbsp;:= AdjustBuffer<span class="br0">&#40;</span>Source^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">Domain</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">ProfileLength</span>.<span class="me1">Value</span>:= Source^.<span class="me1">Credentials</span>.<span class="me1">WIN2K_MODE</span>.<span class="me1">ProfileLength</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">MessageType</span> &nbsp; &nbsp; &nbsp; := Source^.<span class="me1">MessageType</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">LogonCount</span> &nbsp; &nbsp; &nbsp; &nbsp;:= Source^.<span class="me1">LogonCount</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">BadPasswordCount</span> &nbsp;:= Source^.<span class="me1">BadPasswordCount</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">ProfileLogonTime</span> &nbsp; := Source^.<span class="me1">ProfileLogonTime</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">LogoffTime</span> &nbsp; &nbsp; &nbsp; &nbsp; := Source^.<span class="me1">LogoffTime</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">KickOffTime</span> &nbsp; &nbsp; &nbsp; &nbsp;:= Source^.<span class="me1">KickOffTime</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">PasswordLastSet</span> &nbsp; &nbsp;:= Source^.<span class="me1">PasswordLastSet</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">PasswordCanChange</span> &nbsp;:= Source^.<span class="me1">PasswordCanChange</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">PasswordMustChange</span> := Source^.<span class="me1">PasswordMustChange</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">LogonScript</span> &nbsp; &nbsp; &nbsp; := AdjustUnicodeString<span class="br0">&#40;</span>Source^.<span class="me1">LogonScript</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">HomeDirectory</span> &nbsp; &nbsp; := AdjustUnicodeString<span class="br0">&#40;</span>Source^.<span class="me1">HomeDirectory</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">FullName</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:= AdjustUnicodeString<span class="br0">&#40;</span>Source^.<span class="me1">FullName</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">ProfilePath</span> &nbsp; &nbsp; &nbsp; := AdjustUnicodeString<span class="br0">&#40;</span>Source^.<span class="me1">ProfilePath</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">HomeDirectoryDrive</span>:= AdjustUnicodeString<span class="br0">&#40;</span>Source^.<span class="me1">HomeDirectoryDrive</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">LogonServer</span> &nbsp; &nbsp; &nbsp; := AdjustUnicodeString<span class="br0">&#40;</span>Source^.<span class="me1">LogonServer</span>, DynamicBufferDelta<span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; Result^.<span class="me1">UserFlags</span>.<span class="me1">Value</span> &nbsp; := Source^.<span class="me1">UserFlags</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">LogonTime</span> &nbsp; &nbsp; &nbsp; &nbsp; := Source^.<span class="me1">LogonTime</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">SmartCardLogon</span> &nbsp; &nbsp;:= Source^.<span class="me1">SmartCardLogon</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">PrivateDataLen</span> &nbsp; &nbsp;:= Source^.<span class="me1">PrivateDataLen</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">PrivateData</span> &nbsp; &nbsp; &nbsp; := AdjustBuffer<span class="br0">&#40;</span>Source^.<span class="me1">PrivateData</span>, DynamicBufferDelta<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Result^.<span class="me1">Unknown2</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;:= Source^.<span class="me1">Unknown2</span>;</p><p>&nbsp; &nbsp; CopyMemory<span class="br0">&#40;</span>@Result^.<span class="me1">DynamicData</span>, @Source^.<span class="me1">DynamicData</span>, Source^.<span class="me1">TotalLength</span>.<span class="kw3">Length</span> &#8211; <span class="br0">&#40;</span><span class="kw3">SizeOf</span><span class="br0">&#40;</span>Source^<span class="br0">&#41;</span> &nbsp;- <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Source^.<span class="me1">DynamicData</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">function</span> CreateAutoLogonBuffer<span class="br0">&#40;</span><br /> &nbsp; <span class="kw1">const</span> UserName, Domain, Password : <span class="kw4">WideString</span>;<br /> &nbsp; WorkingMode : TWorkingMode;<br /> &nbsp; <span class="kw1">const</span> PrimarySID : JwaWindows.<span class="me1">PSID</span>; &nbsp;TokenGroups : JWaWindows.<span class="me1">PTokenGroups</span>;<br /> &nbsp; <span class="kw1">const</span> Privs : JwaWindows.<span class="me1">PTokenPrivileges</span><br /> &nbsp;<span class="br0">&#41;</span> : PLengthBuffer;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := @CreateAutoLogonBuffer32<span class="br0">&#40;</span>UserName, Domain, Password, WorkingMode, PrimarySID, TokenGroups, Privs<span class="br0">&#41;</span>^.<span class="me1">TotalLength</span>;<br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IsWow64<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; Result := @CreateAutoLogonBuffer64<span class="br0">&#40;</span>PAutoLogonBuffer32<span class="br0">&#40;</span>Result<span class="br0">&#41;</span><span class="br0">&#41;</span>^.<span class="me1">TotalLength</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> FreeAutoLogonBuffer<span class="br0">&#40;</span><span class="kw1">var</span> Buffer : PLengthBuffer<span class="br0">&#41;</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Buffer &lt;&gt; <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IsWow64<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>PAutoLogonBuffer64<span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>^.<span class="me1">UserToken</span>.<span class="me1">Handle</span><span class="br0">&#41;</span><br /> &nbsp; &nbsp; <span class="kw1">else</span><br /> &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>PAutoLogonBuffer32<span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>^.<span class="me1">UserToken</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Buffer := <span class="kw2">nil</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>6) Now we need to open the pipe, send the <em>WLX_SAS_TYPE_AUTHENTICATED</em> SAS to <em>Winlogon</em> and write the data to the pipe:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> SendCredentialThreadProc<span class="br0">&#40;</span>Parameter: PLengthBuffer<span class="br0">&#41;</span>: <span class="kw4">Integer</span>;<br /> <span class="kw1">var</span><br /> &nbsp; PipeHandle : <span class="kw4">THandle</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="nu0">0</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; PipeHandle := OpenPipe;<br /> &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; DoLogonNotifyMessage<span class="br0">&#40;</span>WPARAM_AUTHENTICATED, <span class="nu0">0</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; ProcessPipe<span class="br0">&#40;</span>PipeHandle, Parameter<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>PipeHandle<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; <span class="kw1">on</span> E : EOSError <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; Result := EOsError<span class="br0">&#40;</span>E<span class="br0">&#41;</span>.<span class="me1">ErrorCode</span>;<br /> &nbsp; &nbsp; <span class="kw1">on</span> E : Exception <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; Result := ERROR_GEN_FAILURE;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; <span class="kw3">EndThread</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> <span class="kw1">end</span>;</div><p>7) So, the main procedure will look like this:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">procedure</span> DoLogon<span class="br0">&#40;</span><span class="kw1">const</span> UserName, Domain, Password : <span class="kw4">WideString</span>;<br /> &nbsp; &nbsp; WorkingMode : TWorkingMode;<br /> &nbsp; &nbsp; <span class="kw1">const</span> PrimarySIDStr : <span class="kw4">String</span>; <span class="kw1">const</span> AdditionalSIDS : TStringDynArray;<br /> &nbsp; &nbsp; <span class="kw1">const</span> PriviledgesList : TStringDynArray;<br /> &nbsp; &nbsp; Timeout : <span class="kw4">Cardinal</span><span class="br0">&#41;</span>;<br /> <span class="kw1">var</span><br /> &nbsp; Buffer : PLengthBuffer;<br /> &nbsp; TokenGroups : JWAWindows.<span class="me1">PTokenGroups</span>;<br /> &nbsp; PrimarySID : JWaWindows.<span class="me1">PSID</span>;<br /> &nbsp; ThreadHandle : <span class="kw4">THandle</span>;<br /> &nbsp; LogonThreadHandle : <span class="kw4">THandle</span>;<br /> &nbsp; FUSChecker : TRegistryChecker;<br /> &nbsp; Priviledges : JwaWindows.<span class="me1">PTokenPrivileges</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; CheckLoggedOnUser;<br /> &nbsp; CheckScreenSaver;<br /> &nbsp; TryToConnectToPipe;</p><p>&nbsp; FUSChecker := TRegistryChecker.<span class="me1">Create</span><span class="br0">&#40;</span>FUSKey, <span class="kw2">False</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; PrimarySID := CreateSID<span class="br0">&#40;</span>PrimarySIDStr<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; TokenGroups := GetAdditionalSIDS<span class="br0">&#40;</span>AdditionalSIDS<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Priviledges := CreatePriviledges<span class="br0">&#40;</span>PriviledgesList<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Buffer := CreateAutoLogonBuffer<span class="br0">&#40;</span>UserName, Domain, Password, WorkingMode, PrimarySID, TokenGroups, Priviledges<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LogonThreadHandle := WaitForLogonAsync;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ThreadHandle := SendCredentialsAsync<span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; WaitForThreads<span class="br0">&#40;</span>ThreadHandle, LogonThreadHandle, Timeout<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>ThreadHandle<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>LogonThreadHandle<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FreeAutoLogonBuffer<span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FreePriviledges<span class="br0">&#40;</span>Priviledges<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; FreeAdditionalSIDs<span class="br0">&#40;</span>TokenGroups<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">end</span><br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; _FreeSID<span class="br0">&#40;</span>PrimarySID<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; <span class="kw3">FreeAndNil</span><span class="br0">&#40;</span>FUSChecker<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p><span class="kw1">end</span>;</div> <a  class="downloadlink" href="http://www.remkoweijnen.nl/blog/download/AutoLogonXP-1.0.zip" title="Version1.0 downloaded 116 times">AutoLogonXP 1.0 (116)</a><p>Please note that this sample is free for non-commercial use only. If you require to use it, or it&#8217;s source code in your business projects, please send a request using <a  href="http://www.remkoweijnen.nl/blog/contact/">contact form</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/03/03/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-3-implementation-details/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Autologon user on Windows XP/2003 using AutoReconnect pipe &#8211; part 2 (problems and workarounds)</title><link>http://www.remkoweijnen.nl/blog/2011/03/02/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-2-problems-and-workarounds/</link> <comments>http://www.remkoweijnen.nl/blog/2011/03/02/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-2-problems-and-workarounds/#comments</comments> <pubDate>Tue, 01 Mar 2011 23:10:02 +0000</pubDate> <dc:creator>daNIL</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows 2003]]></category> <category><![CDATA[Windows XP]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1395</guid> <description><![CDATA[In part 1 I&#8217;ve described the theoretical parts needed for a custom autologon application implementation. But there are some practical problems which I will describe here. 1) I use the LsaLogonUser function to log in the user. However, if I do not pass not null for the LocalGroups parameter, msgina.dll fails to process the logon. [...]]]></description> <content:encoded><![CDATA[<p>In <a  href="http://www.remkoweijnen.nl/blog/2011/02/09/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-1-theory/">part 1</a> I&#8217;ve described the theoretical parts needed for a custom autologon application implementation.</p><p>But there are some practical problems which I will describe here.</p><p>1) I use the <a  href="http://msdn.microsoft.com/en-us/library/aa378292(v=vs.85).aspx">LsaLogonUser</a> function to log in the user. However, if I do not pass not null for the <em>LocalGroups</em> parameter, msgina.dll fails to process the logon.</p><p>Why? Because it looks for the <strong>SE_GROUP_LOGON_ID</strong> SID and treat it as logon SID. So we have to add the logon SID manually:<br /> <span id="more-1395"></span></p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> GetAdditionalSIDS<span class="br0">&#40;</span>StringSIDS : TStringDynArray<span class="br0">&#41;</span> : JWAWindows.<span class="me1">PTokenGroups</span>;<br /> <span class="kw1">var</span><br /> &nbsp; Len : <span class="kw4">Integer</span>;<br /> &nbsp; I : <span class="kw4">Integer</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="kw2">nil</span>;</p><p>&nbsp; Len := <span class="kw3">Length</span><span class="br0">&#40;</span>StringSIDS<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Len &gt; <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; Result := GetMemory<span class="br0">&#40;</span><span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^<span class="br0">&#41;</span> + <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Result^.<span class="me1">Groups</span><span class="br0">&#91;</span><span class="nu0">0</span><span class="br0">&#93;</span><span class="br0">&#41;</span> * <span class="br0">&#40;</span>Len<span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">with</span> Result^ <span class="kw1">do</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; GroupCount := Len;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">for</span> I := <span class="kw3">Low</span><span class="br0">&#40;</span>StringSIDs<span class="br0">&#41;</span> <span class="kw1">to</span> <span class="kw3">High</span><span class="br0">&#40;</span>StringSIDs<span class="br0">&#41;</span> <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Groups<span class="br0">&#91;</span>I<span class="br0">&#93;</span> := ConvertSid<span class="br0">&#40;</span>StringSIDs<span class="br0">&#91;</span>i<span class="br0">&#93;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">// hardcode: last sid will be manually added sid, so make it logon sid</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; Groups<span class="br0">&#91;</span><span class="kw3">High</span><span class="br0">&#40;</span>StringSIDs<span class="br0">&#41;</span><span class="br0">&#93;</span>.<span class="me1">Attributes</span> := Groups<span class="br0">&#91;</span><span class="kw3">High</span><span class="br0">&#40;</span>StringSIDs<span class="br0">&#41;</span><span class="br0">&#93;</span>.<span class="me1">Attributes</span> <span class="kw1">or</span> SE_GROUP_LOGON_ID;<br /> &nbsp; &nbsp; &nbsp;<span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>2) In case we want to create a token using <a  href="http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Token/NtCreateToken.html">NtCreateToken</a>, we need to add the <em>Everyone</em> Group (otherwise W<em>inlogon</em> will fail to load the user profile):</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">const</span><br /> &nbsp; EveryoneSID = <span class="st0">&#8216;S-1-1-0&#8242;</span>;</p><p><span class="kw1">procedure</span> CheckAdditionalSIDs<span class="br0">&#40;</span><span class="kw1">var</span> SIDs : TStringDynArray; WorkingMode : TWorkingMode;<br /> &nbsp; <span class="kw1">const</span> UserName, Domain, PrimarySID : <span class="kw4">String</span>; IncludeEveryoneSID : <span class="kw4">Boolean</span><span class="br0">&#41;</span>;<br /> <span class="kw1">var</span><br /> &nbsp; SID : PSID;<br /> &nbsp; SidLength : <span class="kw4">DWORD</span>;</p><p>&nbsp; DomainName : <span class="kw4">PChar</span>;<br /> &nbsp; DomainLen : <span class="kw4">DWORD</span>;</p><p>&nbsp; Name : SID_NAME_USE;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>WorkingMode = wmCreateToken<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IncludeEveryoneSID<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">SetLength</span><span class="br0">&#40;</span>SIDs, <span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> + <span class="nu0">1</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; SIDs<span class="br0">&#91;</span><span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> &#8211; <span class="nu0">1</span><span class="br0">&#93;</span> := EveryoneSID;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; <span class="kw3">SetLength</span><span class="br0">&#40;</span>SIDs, <span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> + <span class="nu0">1</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; SIDs<span class="br0">&#91;</span><span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> &#8211; <span class="nu0">1</span><span class="br0">&#93;</span> := PrimarySID;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> &gt; <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">and</span> <span class="br0">&#40;</span>WorkingMode = wmNormalLogin<span class="br0">&#41;</span> &nbsp;<span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; SidLength := <span class="nu0">0</span>;<br /> &nbsp; &nbsp; DomainLen := <span class="nu0">0</span>;</p><p>&nbsp; &nbsp; LookupAccountName<span class="br0">&#40;</span><span class="kw4">PChar</span><span class="br0">&#40;</span>Domain<span class="br0">&#41;</span>, <span class="kw4">PChar</span><span class="br0">&#40;</span>UserName<span class="br0">&#41;</span>, <span class="kw2">nil</span>, SidLength, <span class="kw2">nil</span>, DomainLen, Name<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="kw3">GetLastError</span> &lt;&gt; ERROR_INSUFFICIENT_BUFFER<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">RaiseLastOSError</span>;</p><p>&nbsp; &nbsp; SID := GetMemory<span class="br0">&#40;</span>SidLength<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; DomainName := GetMemory<span class="br0">&#40;</span>DomainLen<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>LookupAccountName<span class="br0">&#40;</span><span class="kw4">PChar</span><span class="br0">&#40;</span>Domain<span class="br0">&#41;</span>, <span class="kw4">PChar</span><span class="br0">&#40;</span>UserName<span class="br0">&#41;</span>, SID, SidLength, DomainName, DomainLen, Name<span class="br0">&#41;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">SetLength</span><span class="br0">&#40;</span>SIDs, <span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> + <span class="nu0">1</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; SIDs<span class="br0">&#91;</span><span class="kw3">Length</span><span class="br0">&#40;</span>SIDs<span class="br0">&#41;</span> &#8211; <span class="nu0">1</span><span class="br0">&#93;</span> := ConvertSIDToString<span class="br0">&#40;</span>SID<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>DomainName<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; FreeMemory<span class="br0">&#40;</span>SID<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>3) On the X64 versions of Windows XP and all version of Windows 2003 a service process doesn&#8217;t have the <a  href="http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx">SeCreateTokenName</a> privilege. But we can open <em>lsass.exe</em> process and copy it&#8217;s token:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">type</span><br /> &nbsp; TProcessAccessToken = <span class="kw1">record</span><br /> &nbsp; &nbsp; hToken : <span class="kw4">THandle</span>;<br /> &nbsp; &nbsp; hThread : <span class="kw4">THandle</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> AdjustToken;<br /> <span class="kw1">var</span><br /> &nbsp; LsassProcess : <span class="kw4">THandle</span>;<br /> &nbsp; LsassToken : <span class="kw4">THandle</span>;<br /> &nbsp; ProcessToken : TProcessAccessToken;<br /> &nbsp; DuplicatedToken : <span class="kw4">THandle</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Is2003<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; EnableToken<span class="br0">&#40;</span>SE_DEBUG_NAME<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; LsassProcess := FindProcessInSession<span class="br0">&#40;</span><span class="nu0">0</span>, <span class="st0">&#8216;lsass.exe&#8217;</span><span class="br0">&#41;</span>;</p><p>&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>LsassProcess = INVALID_HANDLE_VALUE<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Writeln</span><span class="br0">&#40;</span><span class="st0">&#8216;Unable to find lsass.exe process&#8217;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Abort</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>OpenProcessToken<span class="br0">&#40;</span>LsassProcess, TOKEN_ALL_ACCESS, LsassToken<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>DuplicateTokenEx<span class="br0">&#40;</span>LsassToken, TOKEN_ALL_ACCESS, <span class="kw2">nil</span>, SecurityImpersonation, TokenPrimary, DuplicatedToken<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessToken.<span class="me1">hThread</span> := GetCurrentThread;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ProcessToken.<span class="me1">hToken</span> := DuplicatedToken;</p><p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EnableToken<span class="br0">&#40;</span>SE_ASSIGNPRIMARYTOKEN_NAME<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; NtCheck<span class="br0">&#40;</span>NtSetInformationProcess<span class="br0">&#40;</span>GetCurrentProcess, ProcessAccessToken, @ProcessToken, <span class="kw3">SizeOf</span><span class="br0">&#40;</span>ProcessToken<span class="br0">&#41;</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>DuplicatedToken<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>LsassToken<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>LsassProcess<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>4) In case we create the token manually, we have to specify the logon session. As there is no way to create a session without injecting into <em>lsass.exe</em>, I use a SYSTEM luid as a logon session luid. It has the side-effect of showing the user as SYSTEM regardless of the actual used SID.</p><p>5) Sometimes opening the pipe fails (it may have been connected to some other session&#8217;s <em>winlogon</em>). I try to open the pipe with short timeout and post a disconnect pipe message:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">const</span><br /> &nbsp; WPARAM_DISCONNECT_PIPE = <span class="nu0">20</span>;</p><p><span class="kw1">procedure</span> TryToConnectToPipe;<br /> <span class="kw1">var</span><br /> &nbsp; PipeHandle : <span class="kw4">THandle</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; PipeHandle := INVALID_HANDLE_VALUE;<br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; PipeHandle := OpenPipe<span class="br0">&#40;</span><span class="nu0">100</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; <span class="kw1">on</span> E : EOSError <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw1">if</span> E.<span class="me1">ErrorCode</span> &lt;&gt; ERROR_SEM_TIMEOUT <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">Raise</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; DoLogonNotifyMessage<span class="br0">&#40;</span>WPARAM_DISCONNECT_PIPE, <span class="nu0">0</span><span class="br0">&#41;</span>; <span class="co1">// emulate _WinstationNotifyDisconnectPipe, force pipe to be reconnected.</span><br /> &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>PipeHandle &lt;&gt; INVALID_HANDLE_VALUE<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; CloseHandle<span class="br0">&#40;</span>PipeHandle<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>6) In Windows 2003 (or on XP x64) you should read the BOOL variable from the pipe after you&#8217;ve written to it. But it may not work as <em>Winlogon</em> simply writes the value and then disconnects the pipe, so sometimes the data can not be read (subject to threading issue). I simply ignore the read error, since even if it shows that the credentials were accepted by <em>Winlogon</em>, they may have been rejected by <em>gina</em></p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">procedure</span> ProcessPipe<span class="br0">&#40;</span>PipeHandle : <span class="kw4">THandle</span>; Buffer : PLengthBuffer<span class="br0">&#41;</span>;<br /> <span class="kw1">var</span><br /> &nbsp; NumberOfBytes : <span class="kw4">Cardinal</span>;<br /> &nbsp; Res : <span class="kw4">BOOL</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>WriteFile<span class="br0">&#40;</span>PipeHandle, Buffer, Buffer^.<span class="kw3">Length</span>, @NumberOfBytes, <span class="kw2">nil</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>NumberOfBytes = Buffer^.<span class="kw3">Length</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Is2003<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>ReadFile<span class="br0">&#40;</span>PipeHandle, @Res, <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Res<span class="br0">&#41;</span>, @NumberOfBytes, <span class="kw2">nil</span><span class="br0">&#41;</span> <span class="kw1">and</span><br /> &nbsp; &nbsp; &nbsp;<span class="br0">&#40;</span>NumberOfBytes = <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Res<span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">and</span><br /> &nbsp; &nbsp; <span class="kw1">not</span> Res<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Writeln</span><span class="br0">&#40;</span><span class="st0">&#8216;Credentials were not accepted&#8217;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Abort</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">end</span><br /> <span class="kw1">end</span>;</div><p>7) In some extremely rare cases our process may exit before <em>Winlogon</em> has read the credentials and duplicated them (actuallyI was able to repeat this only under debugging <em>winlogon</em>).</p><p>To be sure that our credentials have been correctly processed, I create a different thread and wait for console logon event:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> WaitForLogonThreadProc<span class="br0">&#40;</span>Parameter: <span class="kw4">Pointer</span><span class="br0">&#41;</span>: <span class="kw4">Integer</span>;<br /> <span class="kw1">var</span><br /> &nbsp; EventFlags : <span class="kw4">Cardinal</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="nu0">0</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; <span class="kw1">repeat</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>WTSWaitSystemEvent<span class="br0">&#40;</span>WTS_CURRENT_SERVER_HANDLE, WTS_EVENT_LOGON, EventFlags<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">until</span> EventFlags <span class="kw1">and</span> WTS_EVENT_LOGON &lt;&gt; <span class="nu0">0</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; <span class="kw1">on</span> E : EOSError <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; Result := EOsError<span class="br0">&#40;</span>E<span class="br0">&#41;</span>.<span class="me1">ErrorCode</span>;<br /> &nbsp; &nbsp; <span class="kw1">on</span> E : Exception <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; Result := ERROR_GEN_FAILURE;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; <span class="kw3">EndThread</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">function</span> WaitForLogonAsync : <span class="kw4">THandle</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="kw3">BeginThread</span><span class="br0">&#40;</span><span class="kw2">nil</span>, <span class="nu0">64</span>*<span class="nu0">1024</span>, @WaitForLogonThreadProc, <span class="kw2">nil</span>, <span class="nu0">0</span>, <span class="kw4">PCardinal</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span>^<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">if</span> Result = <span class="nu0">0</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; <span class="kw3">RaiseLastOSError</span>;<br /> <span class="kw1">end</span>;</div><p>8&zwnj;) If Fast User Switching is enabled and we are on a Workstation OS, W<em>inlogon</em> ignores the <em>WLX_SAS_TYPE_AUTHENTICATED</em> message. However, before execution we can disable the <em>AllowMultipleTSSessions</em> key, and enable it again after the execution. Of course, we do have to remember that under WOW we need to use the special registry flag to access real HKLM hive.</p><p>9) In case session 0 has a logged-on user, W<em>inlogon</em> also ignores the <em>WLX_SAS_TYPE_AUTHENTICATED</em> message. I use <a  href="http://msdn.microsoft.com/en-us/library/aa383840(v=vs.85).aspx">WTSQueryUserToken</a> to get the token of the logged on user. If the function succeeds, then a user is logged on. There are some problems calling this function on 64 bit systems, and they are described in <a  href="http://www.remkoweijnen.nl/blog/?p=1299">Querying a user token under 64 bit version of 2003/XP</a></p><p>10) If the screensaver is running, we&#8217;ll have to wait until someone breaks the screensaver. So we need to stop screensaver programmatically. MSDN recommends posting the WM_CLOSE message to the foreground window, so let&#8217;s do it:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> CreateStopScreenSaverThreadProc<span class="br0">&#40;</span>Parameter: <span class="kw4">Pointer</span><span class="br0">&#41;</span>: <span class="kw4">Integer</span>;<br /> <span class="kw1">var</span><br /> &nbsp; DesktopHandle : <span class="kw4">THandle</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="nu0">0</span>;</p><p>&nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; DesktopHandle &nbsp;:= OpenInputDesktop<span class="br0">&#40;</span>DF_ALLOWOTHERACCOUNTHOOK, <span class="kw2">False</span>, DESKTOP_READOBJECTS<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>DesktopHandle = <span class="nu0">0</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">RaiseLastOSError</span>;<br /> &nbsp; &nbsp; <span class="kw1">try</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>SetThreadDesktop<span class="br0">&#40;</span>DesktopHandle<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>PostMessage<span class="br0">&#40;</span>GetForegroundWindow<span class="br0">&#40;</span><span class="br0">&#41;</span>, WM_CLOSE, <span class="nu0">0</span>, <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">finally</span><br /> &nbsp; &nbsp; &nbsp; CloseDesktop<span class="br0">&#40;</span>DesktopHandle<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; <span class="kw1">except</span><br /> &nbsp; &nbsp; <span class="kw1">on</span> E : EOSError <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; Result := EOsError<span class="br0">&#40;</span>E<span class="br0">&#41;</span>.<span class="me1">ErrorCode</span>;<br /> &nbsp; &nbsp; <span class="kw1">on</span> E : Exception <span class="kw1">do</span><br /> &nbsp; &nbsp; &nbsp; Result := ERROR_GEN_FAILURE;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; <span class="kw3">EndThread</span><span class="br0">&#40;</span>Result<span class="br0">&#41;</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">function</span> CreateStopScreenSaverThread : <span class="kw4">THandle</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; Result := <span class="kw3">BeginThread</span><span class="br0">&#40;</span><span class="kw2">nil</span>, <span class="nu0">64</span>*<span class="nu0">1024</span>, @CreateStopScreenSaverThreadProc, <span class="kw2">nil</span>, <span class="nu0">0</span>, <span class="kw4">PCardinal</span><span class="br0">&#40;</span><span class="kw2">nil</span><span class="br0">&#41;</span>^<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">if</span> Result = <span class="nu0">0</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; <span class="kw3">RaiseLastOSError</span>;<br /> <span class="kw1">end</span>;</p><p><span class="kw1">procedure</span> CheckScreenSaver;<br /> <span class="kw1">var</span><br /> &nbsp; IsScreenSaverRunning : <span class="kw4">BOOL</span>;<br /> &nbsp; ThreadHandle : <span class="kw4">THandle</span>;<br /> &nbsp; Res : <span class="kw4">DWORD</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>SystemParametersInfo<span class="br0">&#40;</span>SPI_GETSCREENSAVERRUNNING, <span class="nu0">0</span>, @IsScreenSaverRunning, <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IsScreenSaverRunning<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; ThreadHandle := CreateStopScreenSaverThread;</p><p>&nbsp; &nbsp; Res := WaitForSingleObject<span class="br0">&#40;</span>ThreadHandle, INFINITE<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Res &lt;&gt; WAIT_OBJECT_0<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; SetLastError<span class="br0">&#40;</span>Res<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; &nbsp; <span class="kw3">RaiseLastOSError</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>GetExitCodeThread<span class="br0">&#40;</span>ThreadHandle, Res<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; SetLastError<span class="br0">&#40;</span>Res<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>Res = ERROR_SUCCESS<span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>11) Although you can send an empty username, some GINA&#8217;s, at least <em>msgina.dll</em> require it to be not-null. So we can use an empty string (#0) instead. You may need to overwrite this procedure to increase compatibility with your GINA:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">procedure</span> CheckGinaCompatibility<span class="br0">&#40;</span><span class="kw1">var</span> UserName : <span class="kw4">String</span>; <span class="kw1">var</span> Domain : <span class="kw4">String</span><span class="br0">&#41;</span>;<br /> <span class="kw1">var</span><br /> &nbsp; Buffer : <span class="kw1">array</span> <span class="br0">&#91;</span><span class="nu0">0</span>..<span class="me1">MAX_COMPUTERNAME_LENGTH</span><span class="br0">&#93;</span> <span class="kw1">of</span> <span class="kw4">Char</span>;<br /> &nbsp; Size : <span class="kw4">DWORD</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>UserName = <span class="st0">&#8221;</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; UserName := <span class="re1">#<span class="nu0">0</span></span>; <span class="co1">// msgina.dll expects user name to be not empty</span></p><p>&nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="br0">&#40;</span>Domain = <span class="st0">&#8221;</span><span class="br0">&#41;</span> <span class="kw1">and</span> <span class="br0">&#40;</span><span class="kw3">AnsiCompareText</span><span class="br0">&#40;</span>GetGINAName, <span class="st0">&#8216;FMLogin.dll&#8217;</span><span class="br0">&#41;</span> = <span class="nu0">0</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; Size := <span class="kw3">SizeOf</span><span class="br0">&#40;</span>Buffer<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; ZeroMemory<span class="br0">&#40;</span>@Buffer, Size<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw3">Win32Check</span><span class="br0">&#40;</span>GetComputerName<span class="br0">&#40;</span>Buffer, Size<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; Domain := Buffer;<br /> &nbsp; <span class="kw1">end</span>;<br /> <span class="kw1">end</span>;</div><p>12) There is incompatibility with some custom GINA&#8217;s, for example, with <a  href="http://www.frontmotion.com/FMLogin/">FMLogin</a>, which subclasses &#8220;Sas Window&#8221;. I&#8217;ve tested the sample with original msgina.dll, so the further compatibility should be tested with the exact gina.</p><p>13) Getting a session process list on x64 may fail if you do not allocate buffers correctly &#8211; described <a  href="http://www.remkoweijnen.nl/blog/2011/01/20/enumerating-session-process-with-ntquerysysteminformation/">here</a></p><p>So we&#8217;re ready to go on to the implementation which will be described in the <a  href="http://www.remkoweijnen.nl/blog/2011/03/03/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-3-implementation-details/">part 3</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/03/02/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-2-problems-and-workarounds/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Autologon user on Windows XP/2003 using AutoReconnect pipe &#8211; part 1 (theory)</title><link>http://www.remkoweijnen.nl/blog/2011/02/28/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-1-theory/</link> <comments>http://www.remkoweijnen.nl/blog/2011/02/28/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-1-theory/#comments</comments> <pubDate>Mon, 28 Feb 2011 09:46:29 +0000</pubDate> <dc:creator>daNIL</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows 2003]]></category> <category><![CDATA[Windows Internals]]></category> <category><![CDATA[Windows XP]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1430</guid> <description><![CDATA[Windows XP introduced the ability to use Fast User Switching (FUS from here on), which is implemented using Terminal Services. But in some cases (i.e. when FUS is not enabled, or when you connect to the console in Windows 2003 server), the Winlogon process in an RDP session needs to transfer credentials to Session 0. [...]]]></description> <content:encoded><![CDATA[<p>Windows XP introduced the ability to use Fast User Switching (FUS from here on), which is implemented using <em>Terminal Services</em>.</p><p>But in some cases (i.e. when FUS is not enabled, or when you connect to the console in Windows 2003 server), the Winlogon process in an RDP session needs to transfer credentials to Session 0.</p><p>Although not documented in MSDN, the process of transferring credentials is described by Keith Brown in the June 2005 issue of MSDN magazine: <a  href="http://msdn.microsoft.com/en-us/magazine/cc163786.aspx">Customizing GINA, Part 2</a>.</p><p><a  href="http://msdn.microsoft.com/en-us/library/aa380577(v=vs.85).aspx">WlxQueryConsoleSwitchCredentials</a> and <a  href="http://msdn.microsoft.com/en-us/library/aa380563(v=vs.85).aspx">WlxGetConsoleSwitchCredentials</a> are used in the transfer with the semi-documented <strong>WLX_SAS_TYPE_AUTHENTICATED</strong> SAS code constant.</p><p>Internally, <em>winlogon.exe</em> uses a Named Pipe, <strong>\\.\Pipe\TerminalServer\AutoReconnect, </strong>to implement both of these functions.</p><p>The pipe format is described in this structure:<br /> <span id="more-1430"></span></p><div class="dean_ch" style="white-space: wrap;"><span class="kw4">typedef</span> <span class="kw4">struct</span> _AUTOLOGON_BUFFER <span class="br0">&#123;</span><br /> &nbsp; &nbsp; DWORD &nbsp; &nbsp; &nbsp;TotalLength;<br /> &nbsp; &nbsp; DWORD &nbsp; &nbsp; &nbsp;SourceProcessId;<br /> &nbsp; &nbsp; HANDLE &nbsp; &nbsp; UserToken;<br /> &nbsp; &nbsp; LUID &nbsp; &nbsp; &nbsp; LogonId;</p><p>&nbsp; &nbsp; QUOTA_LIMITS &nbsp; &nbsp;Quotas;</p><p>&nbsp; &nbsp; <span class="kw4">union</span> <span class="br0">&#123;</span><br /> &nbsp; &nbsp; &nbsp; <span class="kw4">struct</span> _WIN2K_MODE <span class="br0">&#123;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp;PWSTR &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UserName;<br /> &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw4">LONG</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; DomainLength;</p><p>&nbsp; &nbsp; &nbsp; &nbsp;PWSTR &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Domain;<br /> &nbsp; &nbsp; &nbsp; &nbsp;DWORD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ProfileLength;</p><p>&nbsp; &nbsp;<span class="br0">&#125;</span> WIN2K_MODE;</p><p>&nbsp; &nbsp; &nbsp; <span class="kw4">struct</span> _WINXP_MODE <span class="br0">&#123;</span><br /> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PWSTR &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;UserName;<br /> &nbsp; &nbsp; &nbsp; &nbsp;PWSTR &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Domain;</p><p>&nbsp; &nbsp; &nbsp; &nbsp;ULONG &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ProfileLength;<br /> &nbsp; &nbsp; &nbsp; &nbsp;DWORD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Unknown1;<br /> &nbsp; &nbsp;<span class="br0">&#125;</span> WINXP_MODE;</p><p>&nbsp; &nbsp; <span class="br0">&#125;</span> CREDENTIALS;</p><p>&nbsp; &nbsp; DWORD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;MessageType;<br /> &nbsp; &nbsp; USHORT &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; LogonCount;<br /> &nbsp; &nbsp; USHORT &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BadPasswordCount;</p><p>&nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;ProfileLogonTime;<br /> &nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;LogoffTime;<br /> &nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;KickOffTime;<br /> &nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;PasswordLastSet;<br /> &nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;PasswordCanChange;<br /> &nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;PasswordMustChange;</p><p>&nbsp; &nbsp; UNICODE_STRING &nbsp; LogonScript;<br /> &nbsp; &nbsp; UNICODE_STRING &nbsp; HomeDirectory;<br /> &nbsp; &nbsp; UNICODE_STRING &nbsp; FullName;<br /> &nbsp; &nbsp; UNICODE_STRING &nbsp; ProfilePath;<br /> &nbsp; &nbsp; UNICODE_STRING &nbsp; HomeDirectoryDrive;<br /> &nbsp; &nbsp; UNICODE_STRING &nbsp; LogonServer;</p><p>&nbsp; &nbsp; SIZE_T &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; UserFlags;<br /> &nbsp; &nbsp; LARGE_INTEGER &nbsp; &nbsp;LogonTime;<br /> &nbsp; &nbsp; BOOL &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; SmartCardLogon;<br /> &nbsp; &nbsp; ULONG &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PrivateDataLen;<br /> &nbsp; &nbsp; DWORD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;PrivateData;<br /> &nbsp; &nbsp; DWORD &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Unknown2;<br /> <span class="co2">#ifdef _WIN64</span><br /> &nbsp; &nbsp; BYTE &nbsp; &nbsp; &nbsp; &nbsp; DynamicData<span class="br0">&#91;</span><span class="nu0">7900</span><span class="br0">&#93;</span>;<br /> <span class="co2">#else</span><br /> &nbsp; &nbsp; BYTE &nbsp; &nbsp; &nbsp; &nbsp; DynamicData<span class="br0">&#91;</span><span class="nu0">7982</span><span class="br0">&#93;</span>;<br /> <span class="co2">#endif</span><br /> <span class="br0">&#125;</span> AUTOLOGON_BUFFER, *PAUTOLOGON_BUFFER;</div><p>The structure mostly repeats <a  href="http://msdn.microsoft.com/en-us/library/aa381126(v=vs.85).aspx">WLX_CONSOLESWITCH_CREDENTIALS_INFO_V1_0</a>, but you can see some changes in ordering and there are additional fields: <strong>SourceProcessId </strong>(which is used to duplicate the handle) and <strong>TotalLength</strong> (which shows the total length of the structure) have been added.</p><p>All pointers within the structure should be normalized, i.e. should be not pointers, but <em>offsets</em> from the beginning of the structure. This structure is valid for both 32 and 64 bit windows (of course, with alignment and correct sizes respected).</p><p>As you can see, there are some strange things, like declaring pointers as DWORD (<strong>PrivateData</strong>), declaring <strong>UserFlags</strong> as SIZE_T, which is platform dependent, while in the original structure it&#8217;s just ULONG, and so on.</p><p>You might think that the <strong>DynamicData</strong> should be really dynamic, but it isn&#8217;t: <em>Winlogon</em> always creates a variable of <strong>AUTOLOGON_BUFFER</strong> type in it&#8217;s stack or globally. It&#8217;s size is always 0&#215;2000, regardless of platform.</p><p>That&#8217;s why I&#8217;ve made a conditional switch for the <strong>DynamicData</strong> size. So never do like Microsoft guys &#8211; hardcode the size of the variable &#8211; use <strong>sizeof</strong> operator instead <img src='http://www.remkoweijnen.nl/blog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> .</p><p>When the credentials have been transferred to the pipe, session 0 <em>Winlogon</em> needs to get a notification about it. This happens with a <strong>WM_LOGONNOTIFY</strong> message with <strong>WPARAM_AUTHENTICATED</strong> = 14 as WParam.</p><p>When <em>Winlogon</em> recieves this message, it just translates it to <strong>WLX_SAS_TYPE_AUTHENTICATED </strong>SAS code and sends it to <strong>GINA</strong>, forcing it to query the credentials.</p><p>In case of a failure with packing the pipe content or sending it, <em>Winlogon</em> uses the undocumented <strong>_WinstationNotifyDisconnectPipe</strong> function which first verifies the privileges of the calling process and doing the termsrv.dll -&gt;winsrv.dll -&gt; win32k.sys -&gt; winlogon.exe chain, just posts <strong>WM_LOGONNOTIFY</strong> message with <strong>WPARAM_DISCONNECT_PIPE</strong> = 20 as WParam.</p><p>This message forces the pipe to be disconnected and start an asynchronous reconnection to the new client.</p><p>So, at this point we know everything which is necessary to write our own program, which can simulate credentials transfer from a different session instance to zero session instance, enabling us to logon any user to session 0!</p><p>A list of issues and workarounds that I found while writing the program will be described in <a  href="http://www.remkoweijnen.nl/blog/2011/03/02/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-2-problems-and-workarounds/">part 2</a>.</p><p>Parts of the source code and the application itself will be published in <a  href="http://www.remkoweijnen.nl/blog/2011/03/03/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-3-implementation-details/">part 3</a>.</p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/02/28/autologon-user-on-windows-xp2003-using-autoreconnect-pipe-part-1-theory/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> <item><title>The Case of the Citrix Ready Printer Driver</title><link>http://www.remkoweijnen.nl/blog/2011/02/08/the-case-of-the-citrix-ready-printer-driver/</link> <comments>http://www.remkoweijnen.nl/blog/2011/02/08/the-case-of-the-citrix-ready-printer-driver/#comments</comments> <pubDate>Tue, 08 Feb 2011 20:47:44 +0000</pubDate> <dc:creator>Remko</dc:creator> <category><![CDATA[Citrix]]></category> <category><![CDATA[PowerShell]]></category> <category><![CDATA[Terminal Server]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1402</guid> <description><![CDATA[I had a very interesting issue today on a new Citrix XenApp 5 farm. We went into production yesterday and we noticed a number of issues: Printing in general was slow, especially when a user connects to a printer for the first time. User Profiles were rapidly growing in size (from the expected 1-2 MB [...]]]></description> <content:encoded><![CDATA[<p>I had a very interesting issue today on a new Citrix XenApp 5 farm. We went into production yesterday and we noticed a number of issues:</p><ul><li><span style="color: #35383d;">Printing in general was slow, especially when a user connects to a printer for the first time.</span></li><li>User Profiles were rapidly growing in size (from the expected 1-2 MB to over 40 MB).</li><li>Logons took much longer then in the testing period (and since we use a Full Screen Desktop the user doesn&#8217;t see any progress).</li><li>Performance monitoring showed CPU spikes in Word, Excel and IE processes.</li></ul><p><span style="color: #4c4c4c;">I took a look at the profiles first and noticed that the size growth was due to a Xerox subfolder in %APPDATA%:<br /> <span id="more-1402"></span></span></p><p><a  rel="lightbox" href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/02/image2.png" class="thickbox no_icon" title="image"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/02/image_thumb2.png" border="0" alt="image" width="327" height="378" /></a></p><p>An even bigger problem is the huge amount of files in this folder (almost 9000) which of course causes the delay in the logon (while the server is loading the profile).</p><p>This is very bad considering that this driver, the Xerox Global Print Driver is advertised by Xerox as the Terminal Server/Citrix ready printer driver!</p><p>What makes it even worse is that the driver exhibiting this bug, is still the one offered for download on the <a  href="http://www.support.xerox.com/support/global-printer-driver/downloads/enus.html?operatingSystem=wins2003&#038;fileLanguage=en" target="_blank">Xerox Site</a>.</p><p>For the record: I found this bug in version 5.185.19 dated oct. 14, 2010 in the PCL5 and PCL6 versions for both x86 and x64.</p><p>The good news is that I found a newer version that was linked on the Xerox forum, version 5.185.32 dated jan. 5, 2011.</p><p>I tested this version and the bug was fixed there, I cannot yet tell if the CPU Spikes issue was also fixed.</p><p>Here are the links:</p><ul><li><a  href="http://www.support.xerox.com/support/_all-products/file-download/enus.html?contentId=114411" target="_blank">Xerox Global Print Driver x86 PCL5</a></li><li><a  href="http://www.support.xerox.com/support/_all-products/file-download/enus.html?contentId=114412" target="_blank">Xerox Global Print Driver x64 PCL5</a></li><li><a  href="http://www.support.xerox.com/support/_all-products/file-download/enus.html?contentId=114415" target="_blank">Xerox Global Print Driver x86 PCL6</a></li><li><a  href="http://www.support.xerox.com/support/_all-products/file-download/enus.html?contentId=114416" target="_blank">Xerox Global Print Driver x64 PCL6</a></li></ul><p>But after installing this driver we also need to cleanup the (roaming) profiles to get rid of the Xerox folder.</p><p>I wrote a very simple PowerShell script for that:</p><div class="dean_ch" style="white-space: wrap;"><span class="re3">$Share</span> = <span class="st0">&quot;\\ADNRD01\TSProfiles$&quot;</span><br /> <span class="re3">$Xerox</span> = <span class="st0">&quot;\Application Data\Xerox&quot;</span></p><p><span class="kw1">foreach</span> <span class="br0">&#40;</span><span class="re3">$Folder</span> <span class="kw1">in</span> <span class="kw4">gci</span> <span class="re3">$Share</span><span class="br0">&#41;</span><br /> <span class="br0">&#123;</span><br /> &nbsp; <span class="re3">$Path</span> = <span class="re4"><span class="br0">&#91;</span><span class="kw3">System</span>.<span class="kw3">String</span><span class="br0">&#93;</span></span>::<span class="me2">Concat</span><span class="br0">&#40;</span><span class="re3">$Folder</span>.<span class="me1">FullName</span>, <span class="re3">$Xerox</span><span class="br0">&#41;</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">Test-<span class="re1">Path</span></span> <span class="re3">$Path</span><span class="br0">&#41;</span><br /> &nbsp; <span class="br0">&#123;</span><br /> &nbsp; &nbsp; <span class="re0">Remove-<span class="re1">Item</span></span> <span class="re3">$Path</span> <span class="re2">-Recurse</span> <span class="re2">-Force</span><br /> &nbsp; <span class="br0">&#125;</span><br /> <span class="br0">&#125;</span></div> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/02/08/the-case-of-the-citrix-ready-printer-driver/feed/</wfw:commentRss> <slash:comments>2</slash:comments> </item> <item><title>Using Fast User Switching on domain XP computers</title><link>http://www.remkoweijnen.nl/blog/2011/01/30/using-fast-user-switching-on-domain-xp-computers/</link> <comments>http://www.remkoweijnen.nl/blog/2011/01/30/using-fast-user-switching-on-domain-xp-computers/#comments</comments> <pubDate>Sun, 30 Jan 2011 19:42:20 +0000</pubDate> <dc:creator>daNIL</dc:creator> <category><![CDATA[script]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows Internals]]></category> <category><![CDATA[Windows XP]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1344</guid> <description><![CDATA[As you may know, Fast User Switching (FUS) is not available (disabled) on Windows XP computers joined to a domain, Microsoft confirms this in kb280758. However, Microsoft doesn&#8217;t tell us there&#8217;s an undocumented registry value that allows us to have FUS when joined to a domain! To enable FUS you need to set the DWORD registry value HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceFriendlyUI. It can [...]]]></description> <content:encoded><![CDATA[<p>As you may know, Fast User Switching (FUS) is not available (disabled) on Windows XP computers joined to a domain, Microsoft confirms this in <a  href="http://support.microsoft.com/kb/280758" target="_blank">kb280758</a>.</p><p>However, Microsoft doesn&#8217;t tell us there&#8217;s an undocumented registry value that allows us to have FUS when joined to a domain!</p><p>To enable FUS you need to set the <strong>DWORD</strong> registry value <em>HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\ForceFriendlyUI</em>.</p><p>It can also be set by Group Policy at <em>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System.</em></p><p>When the value is set to 1, and <em>LogonType</em> key is also set to 1, it allows you to use a Friendly UI on a computer joined in a domain:<br /> <span id="more-1344"></span></p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image22.png" class="thickbox no_icon" rel="gallery-1344" title="image"><img style="display: inline; border: 0px;" title="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image_thumb22.png" border="0" alt="image" width="424" height="316" /></a></p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image23.png" class="thickbox no_icon" rel="gallery-1344" title="image"><img style="display: inline; border: 0px;" title="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image_thumb23.png" border="0" alt="image" width="424" height="493" /></a></p><p>You can even set <em>AllowMultipleTSSessions</em> value to 1, which will allow you to have multiple users logged on!</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image24.png" class="thickbox no_icon" rel="gallery-1344" title="image"><img style="display: inline; border: 0px;" title="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image_thumb24.png" border="0" alt="image" width="424" height="373" /></a></p><p>The only problem is that the &#8220;Friendly UI&#8221; doesn&#8217;t always allow to logon as a domain user. Only when no-one is logged on you can press the <strong>Ctrl+Alt+Delete</strong> sequence twice to get to the Classis Logon Screen where you can logon with Domain Credentials:</p><p><a  href="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image25.png" class="thickbox no_icon" rel="gallery-1344" title="image"><img style="display: inline; border: 0px;" title="image" src="http://www.remkoweijnen.nl/blog/wp-content/uploads/2011/01/image_thumb25.png" border="0" alt="image" width="424" height="316" /></a></p><p>If we disconnect the console session, for example, by using the <a  href="http://msdn.microsoft.com/en-us/library/cc770592(v=ws.10).aspx">tsdiscon.exe</a> tool we are also able to go to the Classic Logon Screen.</p><p>I&#8217;ve created a few files, which you can place, say to the All Users Desktop (<em>CSIDL_COMMON_DESKTOPDIRECTORY)</em> folder (i.e. &#8220;C:\Documents and Settings\All Users\Desktop&#8221;) to make them available to all users:</p> <a  class="downloadlink" href="http://www.remkoweijnen.nl/blog/download/Friendly-UI.zip" title="Version1.0 downloaded 286 times">Friendly UI batch tools (286)</a><p>There is also a slightly modified version of &#8220;<a  href="http://www.remkoweijnen.nl/blog/2008/11/26/executing-a-fast-user-switch-programmatically-part-2/">Switch User</a>&#8221; application, which now adjust the value of <em>AllowMultipleTSSessions, LogonType</em> and <em>ForceFriendlyUI </em>keys programmatically (if required) for the time needed to logon the user and then reverts them back.</p><p>It also supports a <strong>/Server=ServerName</strong> option, which allows you to execute it on another machine.</p><p>Please note that you cannot log on domain users in this way!</p> <a  class="downloadlink" href="http://www.remkoweijnen.nl/blog/download/SwitchUser-1-21.zip" title="Version1.21 downloaded 213 times">Switch User 1.21 (213)</a><p>P.S. &#8220;Bugs&#8221;</p><p>1) If you&#8217;ve logged on as a domain user, and are set the logon type to friendly UI, be careful: if you lock your current session, you won&#8217;t be able to return to it in a normal way &#8211; &#8220;Friendly UI&#8221; doesn&#8217;t support domain logged users and you just won&#8217;t be listed in a users list! But, of course, you can log in as a different user, start Task Manager, click on &#8220;Users&#8221; tab, and connect to your original session manually. You can also use &#8220;&#8221;Turn Friendly UI Off.bat&#8221; file to switch Friendly UI off and be able to lock your workstation normally.</p><p>2) If you connect via RDP session, you&#8217;ll still get a &#8220;Friendly UI&#8221;. It won&#8217;t be fully functional as it was not designed to be used from RDP session.</p><p>3) Explorer doesn&#8217;t show you &#8220;Switch user&#8221; option when you try to logoff the current user, but you can lock workstation by using &#8220;Window+L&#8221; shortcut or use the batch file above.</p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/01/30/using-fast-user-switching-on-domain-xp-computers/feed/</wfw:commentRss> <slash:comments>0</slash:comments> </item> <item><title>Querying a user token under 64 bit version of 2003/XP</title><link>http://www.remkoweijnen.nl/blog/2011/01/29/querying-a-user-token-under-64-bit-version-of-2003xp/</link> <comments>http://www.remkoweijnen.nl/blog/2011/01/29/querying-a-user-token-under-64-bit-version-of-2003xp/#comments</comments> <pubDate>Sat, 29 Jan 2011 21:17:52 +0000</pubDate> <dc:creator>daNIL</dc:creator> <category><![CDATA[Delphi]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Strange Error]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Windows 2003]]></category> <category><![CDATA[Windows Internals]]></category> <category><![CDATA[Windows XP]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1299</guid> <description><![CDATA[If you want to obtain a user&#8217;s token in a Terminal Server or Citrix session (eg to launch a process in a session) you can call the WTSQueryUserToken function. On the x64 versions of Windows XP and Server 2003 this function fails however and returns ERROR_INSUFFICIENT_BUFFER (&#8220;The data area passed to a system call is [...]]]></description> <content:encoded><![CDATA[<p>If you want to obtain a user&#8217;s token in a Terminal Server or Citrix session (eg to launch a process in a session) you can call the <a  href="http://msdn.microsoft.com/en-us/library/aa383840(v=vs.85).aspx">WTSQueryUserToken</a> function.</p><p>On the x64 versions of Windows XP and Server 2003 this function fails however and returns <strong>ERROR_INSUFFICIENT_BUFFER</strong> (&#8220;<em>The data area passed to a system call is too small</em>.&#8221;) when called from a 32 bit process.</p><p>Internally WTSQueryUserToken calls the undocumented function <a  href="http://msdn.microsoft.com/en-us/library/aa383827(v=vs.85).aspx">WinstationQueryInformationW</a> with the <strong>WinStationUserToken</strong> class (14) and passing a <a  href="http://msdn.microsoft.com/en-us/library/cc248658(PROT.10).aspx">WINSTATIONUSERTOKEN</a> struct, filled with caller ProcessId and ThreadId.</p><p>But on x64 Windows the size of this structure is 24 bytes, while on 32 bit Windows the size of the structure is 12 bytes!</p><p><span id="more-1299"></span>Ok, let&#8217;s try to call <a  href="http://msdn.microsoft.com/en-us/library/aa383827(v=vs.85).aspx">WinstationQueryInformationW</a> function directly:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">type</span><br /> &nbsp; THANDLE_64 = <span class="kw1">record</span><br /> &nbsp; &nbsp; Handle : <span class="kw4">THandle</span>;<br /> &nbsp; &nbsp; Alignment : <span class="kw4">DWORD</span>;<br /> &nbsp; <span class="kw1">end</span>;</p><p><span class="kw1">function</span> GetUserToken<span class="br0">&#40;</span>SessionId : <span class="kw4">DWORD</span>; out TokenHandle : <span class="kw4">THandle</span><span class="br0">&#41;</span> : <span class="kw4">BOOL</span>;<br /> <span class="kw1">var</span><br /> &nbsp; WINSTATIONUSERTOKEN64 : <span class="kw1">record</span><br /> &nbsp; &nbsp; ProcessId : THANDLE_64;<br /> &nbsp; &nbsp; ThreadId : THANDLE_64;<br /> &nbsp; &nbsp; TokenHandle : THANDLE_64;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; Res : <span class="kw4">DWORD</span>;<br /> &nbsp; RetLength : <span class="kw4">DWORD</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IsWow64<span class="br0">&#41;</span> <span class="kw1">then</span> <span class="kw1">begin</span><br /> &nbsp; &nbsp; <span class="kw1">with</span> WINSTATIONUSERTOKEN64 <span class="kw1">do</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; ProcessId.<span class="me1">Handle</span> := GetCurrentProcessId;<br /> &nbsp; &nbsp; &nbsp; ProcessId.<span class="me1">Alignment</span> := <span class="nu0">0</span>;</p><p>&nbsp; &nbsp; &nbsp; ThreadId.<span class="me1">Handle</span> := GetCurrentThreadId;<br /> &nbsp; &nbsp; &nbsp; ThreadId.<span class="me1">Alignment</span> := <span class="nu0">0</span>;</p><p>&nbsp; &nbsp; &nbsp; TokenHandle.<span class="me1">Handle</span> := <span class="nu0">0</span>;<br /> &nbsp; &nbsp; &nbsp; TokenHandle.<span class="me1">Alignment</span> := <span class="nu0">0</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;<br /> &nbsp; &nbsp; Result := WinstationQueryinformationW<span class="br0">&#40;</span>WTS_CURRENT_SERVER, SessionId, WinstationUserToken, @WINSTATIONUSERTOKEN64, <span class="kw3">SizeOf</span><span class="br0">&#40;</span>WINSTATIONUSERTOKEN64<span class="br0">&#41;</span>, RetLength<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Result<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; TokenHandle := WINSTATIONUSERTOKEN64.<span class="me1">TokenHandle</span>.<span class="me1">Handle</span>;</p><p>&nbsp; <span class="kw1">end</span> <span class="kw1">else</span><br /> &nbsp; &nbsp; Result := WTSQueryUserToken<span class="br0">&#40;</span>SessionId, TokenHandle<span class="br0">&#41;</span>;<br /> <span class="kw1">end</span>;</div><p>But this call returns <strong>ERROR_INSUFFICIENT_BUFFER</strong> again! Why?</p><p>The reason is that <a  href="http://msdn.microsoft.com/en-us/library/aa383827(v=vs.85).aspx">WinstationQueryInformationW</a> does some buffer checks/adjustments, before calling the real function, <a  href="http://msdn.microsoft.com/en-us/library/cc248834(PROT.10).aspx">RpcWinStationQueryInformation</a>.</p><p>Unfortunately, in some cases the buffer size is (incorrectly) hardcoded while  the receiver (termsrv.dll) in many cases allows any value greater then required.</p><p>The buffer check is done in an internal function called ValidUserBuffer:</p><div class="dean_ch" style="white-space: wrap;">bool __stdcall ValidUserBuffer<span class="br0">&#40;</span><span class="kw4">unsigned</span> <span class="kw4">int</span> BufferSize, <span class="kw4">int</span> WinStationInformationClass<span class="br0">&#41;</span><br /> <span class="br0">&#123;</span></p><p>&nbsp; <span class="kw1">switch</span> <span class="br0">&#40;</span> WinStationInformationClass <span class="br0">&#41;</span><br /> &nbsp; <span class="br0">&#123;</span><br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">25</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= 0&#215;48;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">0</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">8</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">2</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">568</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">5</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">264</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">7</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">11</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">12</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">13</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">16</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">17</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">18</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">19</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">20</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">true</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">9</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">652</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">10</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">4</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">26</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">16</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">27</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= 0xCC;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">29</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= 0&#215;20;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">34</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= 0&#215;60;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">33</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= 0&#215;600;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">32</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">35</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">36</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= <span class="nu0">1</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">24</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">28</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">30</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">31</span>:<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">37</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= <span class="nu0">4</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">8</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">1216</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">6</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">2296</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">14</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">12</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">21</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize &gt;= <span class="nu0">9</span>;<br /> &nbsp; &nbsp; <span class="kw1">default</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="kw2">false</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">1</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">2664</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">3</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">300</span>;<br /> &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">4</span>:<br /> &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> BufferSize == <span class="nu0">736</span>;<br /> &nbsp; <span class="br0">&#125;</span><br /> <span class="br0">&#125;</span></div><p>So, as you can see, there is no way to succeed using the original winsta.dll!</p><p>The only workaround is to patch it (and possibly distribute it with your app) or use <a  href="http://msdn.microsoft.com/en-us/library/cc248834(PROT.10).aspx">RpcWinStationQueryInformation</a> directly.</p><p>The code below works fine:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw1">function</span> GetUserToken<span class="br0">&#40;</span>SessionId : <span class="kw4">DWORD</span>; out TokenHandle : <span class="kw4">THandle</span><span class="br0">&#41;</span> : <span class="kw4">BOOL</span>;<br /> <span class="kw1">var</span><br /> &nbsp; WINSTATIONUSERTOKEN64 : <span class="kw1">record</span><br /> &nbsp; &nbsp; ProcessId : THANDLE_64;<br /> &nbsp; &nbsp; ThreadId : THANDLE_64;<br /> &nbsp; &nbsp; TokenHandle : THANDLE_64;<br /> &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; Res : <span class="kw4">DWORD</span>;<br /> &nbsp; RetLength : <span class="kw4">DWORD</span>;<br /> <span class="kw1">begin</span><br /> &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>IsWow64<span class="br0">&#41;</span> <span class="kw1">then</span> <span class="kw1">begin</span><br /> &nbsp; &nbsp; <span class="kw1">with</span> WINSTATIONUSERTOKEN64 <span class="kw1">do</span><br /> &nbsp; &nbsp; <span class="kw1">begin</span><br /> &nbsp; &nbsp; &nbsp; ProcessId.<span class="me1">Handle</span> := GetCurrentProcessId;<br /> &nbsp; &nbsp; &nbsp; ProcessId.<span class="me1">Alignment</span> := <span class="nu0">0</span>;</p><p>&nbsp; &nbsp; &nbsp; ThreadId.<span class="me1">Handle</span> := GetCurrentThreadId;<br /> &nbsp; &nbsp; &nbsp; ThreadId.<span class="me1">Alignment</span> := <span class="nu0">0</span>;</p><p>&nbsp; &nbsp; &nbsp; TokenHandle.<span class="me1">Handle</span> := <span class="nu0">0</span>;<br /> &nbsp; &nbsp; &nbsp; TokenHandle.<span class="me1">Alignment</span> := <span class="nu0">0</span>;<br /> &nbsp; &nbsp; <span class="kw1">end</span>;</p><p>&nbsp; &nbsp; Result := RpcWinStationQueryInformation<span class="br0">&#40;</span>WinStationGetLocalServerHandle, Res, SessionId, WinStationUserToken, @WINSTATIONUSERTOKEN64, <span class="kw3">SizeOf</span><span class="br0">&#40;</span>WINSTATIONUSERTOKEN64<span class="br0">&#41;</span>, RetLength<span class="br0">&#41;</span>;<br /> &nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span>Result<span class="br0">&#41;</span> <span class="kw1">then</span><br /> &nbsp; &nbsp; &nbsp; TokenHandle := WINSTATIONUSERTOKEN64.<span class="me1">TokenHandle</span>.<span class="me1">Handle</span><br /> &nbsp; &nbsp; <span class="kw1">else</span><br /> &nbsp; &nbsp; &nbsp; SetLastError<span class="br0">&#40;</span>RtlNtStatusToDosError<span class="br0">&#40;</span>Res<span class="br0">&#41;</span><span class="br0">&#41;</span>;<br /> &nbsp; <span class="kw1">end</span> <span class="kw1">else</span><br /> &nbsp; &nbsp; Result := WTSQueryUserToken<span class="br0">&#40;</span>SessionId, TokenHandle<span class="br0">&#41;</span>;<br /> <span class="kw1">end</span>;</div><p>However to use the RpcWinStationQueryInformation you need the MIDL compiler, which supports only C/C++, to generate the code.</p><p>Of course it can be done using Delphi but that is out of scope for this article.</p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/01/29/querying-a-user-token-under-64-bit-version-of-2003xp/feed/</wfw:commentRss> <slash:comments>13</slash:comments> </item> <item><title>Script to install all print drivers on Citrix or Terminal Server</title><link>http://www.remkoweijnen.nl/blog/2011/01/25/script-to-install-all-print-drivers-on-citrix-or-terminal-server/</link> <comments>http://www.remkoweijnen.nl/blog/2011/01/25/script-to-install-all-print-drivers-on-citrix-or-terminal-server/#comments</comments> <pubDate>Tue, 25 Jan 2011 14:24:14 +0000</pubDate> <dc:creator>Remko</dc:creator> <category><![CDATA[Altiris]]></category> <category><![CDATA[Citrix]]></category> <category><![CDATA[PowerShell]]></category> <category><![CDATA[Terminal Server]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1292</guid> <description><![CDATA[I wrote a PowerShell script to install all printer drivers on a Citrix or Terminal Server. Actually the script isn&#8217;t specific to Citrix or Terminal Server but on such environments we need to preload all drivers because users do not have the permissions to do that. I have chosen for PowerShell because you can do [...]]]></description> <content:encoded><![CDATA[<p>I wrote a PowerShell script to install all printer drivers on a Citrix or Terminal Server.</p><p>Actually the script isn&#8217;t specific to Citrix or Terminal Server but on such environments we need to preload all drivers because users do not have the permissions to do that.</p><p>I have chosen for PowerShell because you can do it in a one-liner which makes it easy to run this script from my Altiris server on all Citrix Servers.</p><p>The idea is that we enumerate all the shared printers on a Printer Server and make a connection to each printer. This will make sure that the driver is installed if it wasn&#8217;t already present.</p><p>The script could even be scheduled to enforce that newly added printer drivers are added to each Citrix Server.</p><p><span id="more-1292"></span></p><p>To enumerate all printers we can use the <a  href="http://msdn.microsoft.com/en-us/library/aa394363(v=vs.85).aspx" target="_blank">Win32_Printer</a> WMI class like this:</p><div class="dean_ch" style="white-space: wrap;"><span class="re0">Get-<span class="re1">WmiObject</span></span> win32_printer <span class="re2">-ComputerName</span> <span class="st0">&quot;MYSERVER&quot;</span></div><p>It&#8217;s possible that some printers are not shared so we are going to filter that out using the -filter parameter:</p><div class="dean_ch" style="white-space: wrap;"><span class="re0">Get-<span class="re1">WmiObject</span></span> win32_printer <span class="re2">-ComputerName</span> <span class="st0">&quot;MYSERVER&quot;</span></div><p>Our next step is to make a printer connection and MSDN shows that the win32_printer class has an <a  href="http://msdn.microsoft.com/en-us/library/aa384769(v=vs.85).aspx" target="_blank">AddPrinterConnection</a> method.</p><p>So I first tried:</p><div class="dean_ch" style="white-space: wrap;"><span class="re3">$Wmi</span>.<span class="me1">AddPrinterConnection</span><span class="br0">&#40;</span><span class="st0">&quot;\\MYSERVER\PRINTER&quot;</span><span class="br0">&#41;</span><br /> Method invocation failed because <span class="re4"><span class="br0">&#91;</span><span class="kw3">System</span>.<span class="me1">Object</span><span class="br0">&#91;</span><span class="br0">&#93;</span><span class="br0">&#93;</span></span> doesn<span class="st0">&#8216;t contain a method named &#8216;</span>AddPrinterConnection<span class="st0">&#8216;.<br /> At line:1 char:26<br /> + $Wmi.AddPrinterConnection &lt;&lt;&lt;&lt; (&quot;\\SERVER\PRINTER&quot;)<br /> &nbsp; &nbsp; + CategoryInfo &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;: InvalidOperation: (AddPrinterConnection:String) [], RuntimeException<br /> &nbsp; &nbsp; + FullyQualifiedErrorId : MethodNotFound</span></div><p>I am not sure of the exact reason but obviously there is no access to the AddPrinterConnection method.</p><p>This works though:</p><div class="dean_ch" style="white-space: wrap;"><span class="re3">$Wmi</span> = <span class="br0">&#40;</span><span class="re4"><span class="br0">&#91;</span><span class="kw3">wmiclass</span><span class="br0">&#93;</span></span><span class="st0">&quot;Win32_Printer&quot;</span><span class="br0">&#41;</span><br /> <span class="re3">$Wmi</span>.<span class="me1">AddPrinterConnection</span><span class="br0">&#40;</span><span class="st0">&quot;\\MYSERVER\PRINTER&quot;</span><span class="br0">&#41;</span></div><p>Now we can use the For-Each Object to make a connection to each printer using the __SERVER property for the servername and the ShareName property for the Printername:</p><p>Once again I met a PowerShell oddity: I couldn&#8217;t use AddPrinterConnection(&#8220;\\$_.__SERVER\$_.ShareName&#8221;) so I used:</p><div class="dean_ch" style="white-space: wrap;"><span class="re3">$Wmi</span>.<span class="me1">AddPrinterConnection</span><span class="br0">&#40;</span> <span class="re4"><span class="br0">&#91;</span><span class="kw3">string</span><span class="br0">&#93;</span></span>::<span class="me2">Concat</span><span class="br0">&#40;</span><span class="st0">&quot;\\&quot;</span>, <span class="re3">$_</span>.__SERVER, <span class="st0">&quot;\&quot;</span>, <span class="re3">$_</span>.<span class="me1">ShareName</span><span class="br0">&#41;</span> <span class="br0">&#41;</span></div><p>And finally we put the whole thing in a one liner, replacing double quote (&#8220;) with (&#8216;) and using the gwmi alias to make the line shorter:</p><div class="dean_ch" style="white-space: wrap;"><span class="kw3">powershell</span>.<span class="me1">exe</span> <span class="st0">&quot;&amp; { $Wmi = ([wmiclass]&#8216;Win32_Printer&#8217;) ; $Wmi.Scope.Options.EnablePrivileges = $true; gwmi win32_printer -ComputerName &#8216;ADNRD02&#8242; -Filter &#8216;shared=true&#8217; | foreach {$Wmi.AddPrinterConnection( [string]::Concat(&#8216;\\&#8217;, $_.__SERVER, &#8216;\&#8217;, $_.ShareName) )} }&quot;</span></div><p><span style="color: #ff0000;"><strong>EDIT:</strong></span> In order to load new driver we need to enable the SeLoadDriverPrivilege. I have corrected the code above,  see <a  href="http://www.remkoweijnen.nl/blog/2011/01/27/enabling-privileges-for-wmi-in-powershell/">Enabling Privileges for WMI in PowerShell</a> for an explanation.</p> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/01/25/script-to-install-all-print-drivers-on-citrix-or-terminal-server/feed/</wfw:commentRss> <slash:comments>5</slash:comments> </item> <item><title>SasLibEx Screencast</title><link>http://www.remkoweijnen.nl/blog/2011/01/19/saslibex-screencast/</link> <comments>http://www.remkoweijnen.nl/blog/2011/01/19/saslibex-screencast/#comments</comments> <pubDate>Wed, 19 Jan 2011 21:36:44 +0000</pubDate> <dc:creator>Remko</dc:creator> <category><![CDATA[Citrix]]></category> <category><![CDATA[Programming]]></category> <category><![CDATA[Terminal Server]]></category> <category><![CDATA[Vista]]></category> <category><![CDATA[Windows 2008]]></category> <category><![CDATA[Windows 2008 R2]]></category> <category><![CDATA[Windows 7]]></category> <category><![CDATA[SasLibEx]]></category><guid isPermaLink="false">http://www.remkoweijnen.nl/blog/?p=1238</guid> <description><![CDATA[I just recorded a SasLibEx Screencast, it shows some of the very powerfull features of SasLibEx. The following features are shown: Simulate Ctrl Alt Del (Secure Attention Sequence) Cancel Ctrl Alt Del Lock Workstation Unlock Workstation (without credentials) Disable Ctrl Alt Del Enable Ctrl Alt Del again Cancel pending UAC request Is Desktop Locked SasLibEx [...]]]></description> <content:encoded><![CDATA[<p>I just recorded a SasLibEx Screencast, it shows some of the very powerfull features of SasLibEx.</p><p>The following features are shown:</p><ul><li><span style="color: #35383d;">Simulate Ctrl Alt Del (Secure Attention Sequence)</span></li><li><span style="color: #35383d;">Cancel Ctrl Alt Del</span></li><li><span style="color: #35383d;">Lock Workstation</span></li><li><span style="color: #35383d;">Unlock Workstation (without credentials)</span></li><li><span style="color: #35383d;">Disable Ctrl Alt Del</span></li><li><span style="color: #35383d;">Enable Ctrl Alt Del again</span></li><li><span style="color: #35383d;">Cancel pending UAC request</span></li><li><span style="color: #35383d;">Is Desktop Locked</span></li></ul><div id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:8c371d04-5435-4a86-a54a-5611345258b5" class="wlWriterEditableSmartContent" style="margin: 0px; display: inline; float: none; padding: 0px;"><div><object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="448" height="252" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"><param name="src" value="http://www.youtube.com/v/Nyl4_ECe5xI?hl=en&amp;hd=1" /><embed type="application/x-shockwave-flash" width="448" height="252" src="http://www.youtube.com/v/Nyl4_ECe5xI?hl=en&amp;hd=1"></embed></object></div><div style="width: 448px; clear: both; font-size: 0.8em;">SasLibEx Feature Demo #1</div></div> ]]></content:encoded> <wfw:commentRss>http://www.remkoweijnen.nl/blog/2011/01/19/saslibex-screencast/feed/</wfw:commentRss> <slash:comments>1</slash:comments> </item> </channel> </rss>
