3

The code provided by Jon in the following thread seems to illustrate exactly what I want to do

Running a process at the Windows 7 Welcome Screen

Unfortunately it's in C#, a language I don't know at all. I'm trying to translate the code to Pascal (a recent version of Lazarus on Windows 7). From reading between the lines, I think I may have got a lot of it - however, I'll only know if I've got it wrong when it fails to do its job. At the moment it is failing to compile at the following point.

if (!DuplicateTokenEx(userToken, 0x10000000, ref tokenAttributes,   
SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, TOKEN_TYPE.TokenImpersonation,
out newToken)) {
log("ERROR: DuplicateTokenEx returned false - "

My Pascal version:

If Not DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, tokenAttributes,
SecurityImpersonation, TokenPrimary, newToken) then
  Writeln(DLog, 'Failed to duplicate security token'); 

Lazarus throws an error on the fifth of the six parameters.

dmain.pas(189,110) Error: Incompatible type for arg no. 5: Got "TOKEN_TYPE", expected "_TOKEN_TYPE" - which indicates I haven't understood what the parameters are doing. (Changing parameter 5 to TokenImpersonation throws the same error.)

Further down, I get even more lost:

tokPrivs.Privileges = new LUID_AND_ATTRIBUTES[1];
tokPrivs.Privileges[0].Luid = seDebugNameValue;
tokPrivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

I can see that the structure type LUID_AND_ATTRIBUTES is in the Windows API but it seems it's not recognised by Lazarus.

In short, I'm groping around in the dark. I've tried Googling for "C# for Pascal programmers" but didn't find anything helpful. Learning C# isn't a trivial undertaking, so I be would really grateful for any hints on the differences between it and Object Pascal, and how to translate this code.

Edit: Unfinished code as requested.

function RunOurProcess(ProgramName: String): Boolean;
var
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
NewToken, Token, UserToken: THandle;
WPID: DWord;
ThreadAttributes, TokenAttributes: TSecurityAttributes;
TOKPrivs: TTokenPrivileges;
begin
    FillChar(StartInfo, SizeOf(TStartupInfo), #0);
    FillChar(ProcInfo, SizeOf(TProcessInformation), #0);
    StartInfo.cb:= SizeOf(TStartupInfo);
    { Insert handle of current desktop - without this, GUI app is not visible!
      To appear before logon, lpDesktop value should be 'winsta0\WinLogon' }
    StartInfo.lpDesktop:= PChar('winsta0\WinLogon');
    // Save the process ID of the WinLogon process
    WPID:= FindInProcesses('Winlogon.exe');
    // Get the handle of this
    Token:= OpenProcess(TOKEN_QUERY or TOKEN_IMPERSONATE or TOKEN_DUPLICATE, False, WPID);
    // Open a process token using the handle above
    If OpenProcessToken(Token, TOKEN_QUERY or TOKEN_IMPERSONATE or TOKEN_DUPLICATE, UserToken) then
    Writeln(DLog, 'Opened process token for WinLogon')
        else
    Writeln(DLog, 'Failed to open process token for WinLogon');
    // Create a new token
    NewToken:= 0;
    tokenAttributes.nLength:= SizeOf(tokenAttributes);
    threadAttributes.nLength:= SizeOf(threadAttributes);
    If Not DuplicateTokenEx(UserToken, MAXIMUM_ALLOWED, tokenAttributes, SecurityImpersonation, TokenImpersonation, newToken) then
    Writeln(DLog, 'Failed to duplicate security token');
    // Elevate the privileges of the token
    AdjustTokenPrivileges(NewToken, False, {NewState, BufferLength, PreviousState, ReturnLength});
    // LogOnUser
    // If successful, CreateProcessAsUser
    // In progress - code below needs to go before 'CreateProcessAsUser'
    StartInfo.cb:= SizeOf(TStartupInfo);
    // Insert handle of current desktop - without this, GUI app is not visible!
    StartInfo.lpDesktop:= PChar('winsta0\WinLogon');
end; // RunOurProcess            

I notice now that I get the following error if I try to find the declaration for "DuplicateTokenEx"

C:\lazarus\fpc\2.6.1\source\packages\winunits-jedi\src\jwawindows.pas(366,5) Error: include file not found "JwaLmErr.pp"

Community
  • 1
  • 1
  • You have not told us any of the types that you are using. Also, where are you getting `DuplicateTokenEx` from? Please supply a complete program. – David Heffernan Mar 23 '13 at 18:12
  • Thanks for your response, David. I'm trying to post my complete unit but the only visible option is "Add Comment" and the page tells me this is too long by more than 8000 characters. Apologies for my ignorance, but how do I respond other than by a comment? – John Saltwell Mar 23 '13 at 20:08
  • Could you cut the program down to as small a size as possible and post in the question. – David Heffernan Mar 23 '13 at 20:11
  • There's JEDI here too. It's a shame you didn't show the entire program so that I could try and compile it. Where is your uses clause? The solution to your errors will be found by reading the JEDI headers. Anyway, you seem to think your problem is in understanding the C#. That's not your problem. You can read the MSDN docs just fine. No need to understand C#. Your problem is calling Win32 API from FPC. – David Heffernan Mar 23 '13 at 20:31
  • You're right of course, but that assumes a fairly high level of expertise on my part. Despite years of programming, it's (of necessity) a part time occupation for me, and I've never been that good. If I knew enough about Windows security then digesting the Windows API would do it. In practice, I find most MSDN articles on topics such as security incomprehensible once they get into C code - hence the necessity to look at how someone else has done the same task and try to translate their code to a language I understand. (Uploaded only a sample here because the backticks formatting didn't work.) – John Saltwell Mar 24 '13 at 11:46

1 Answers1

1

Here's how to solve the compilation problems.

The call to DuplicateTokenEx actually fails on the third parameter. Looking at the declaration, it is LPSECURITY_ATTRIBUTES which is ^TSecurityAttributes. Now, tokenAttributes is of type TSecurityAttributes so you need to pass its address:

If Not DuplicateTokenEx(..., @tokenAttributes, ...) then

And similarly in the call to AdjustTokenPrivileges. The C# code is:

AdjustTokenPrivileges(newToken, false, ref tokPrivs, 0, IntPtr.Zero, IntPtr.Zero)

Translated to Pascal that would be:

AdjustTokenPrivileges(NewToken, False, @TOKPrivs, 0, nil, nil)

I've no idea whether or not the code will solve your problem. I think that's beyond the remit of this question – at least that's my excuse and I'm sticking to it!

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks for your help! Definitely a useful hint there so I'll try that and bear your example in mind when tackling the rest. – John Saltwell Mar 23 '13 at 20:48
  • Still can't get the program to compile so I've uploaded the whole thing to Dropbox in case David (or any other expert feeling charitable!) has a chance to glance over it. Any further hints would be gratefully received. https://www.dropbox.com/sh/ulvurtluroc4u34/Vhbl_tkgZc – John Saltwell Mar 24 '13 at 12:14
  • Surely I answered the question that you asked. How far do you want this to go? – David Heffernan Mar 24 '13 at 12:16
  • Anyway, the problem is the order of your uses clauses. You needs Windows to be listed before JwaWindows. I honestly believe that I have answered the question that you asked. You'll also need to fix the `AdjustTokenPrivileges` call as I described. And then the `CreateProcessAsUser`. But you can work that out yourself. The functions are declared in `jwawinbase`. When you have type mismatch of the parameters, look at the declaration and make sure the parameters match. Also do this hand in hand with the MSDN docs. – David Heffernan Mar 24 '13 at 12:30
  • Thanks. I thought I'd done as you advise but should double check my code. Tried to mark your answer as helpful but it seems I need more points before that's allowed. – John Saltwell Mar 24 '13 at 14:43
  • John: instead of manipulation uses clauses, you can also prefix (qualify) all types with unit name. So windows.TOKEN_TYPE, if you want it from unit Windows etc. – Marco van de Voort Mar 27 '13 at 11:26
  • @MarcovandeVoort You can do that too. But I bet that when you code you are particular with the order of your uses. – David Heffernan Mar 27 '13 at 11:31
  • You'd be surprised. I use quite a lot of qualification. I'm also quite fanatical about encapsulation (by modularization, not the OO principle) and keeping the import scope small (avoid units that don't belong there, and managing what is imported e.g. by using a new unit with a type alias rather than importing an unit with many identifiers). In that sense I'm not the avg Pascal programmer, the Modula2 roots shining through probably. In general I only manage unit order in the main program (.dpr), because that has relation to module initialization – Marco van de Voort Mar 27 '13 at 12:49
  • @MarcovandeVoort I also have a Modula-2 background, although that was a very long time ago. I do very much find that Delphi's `use` is sorely lacking. What's lacking in the using qualification approach is for the language/compiler to enforce it. – David Heffernan Mar 27 '13 at 12:56
  • Yup. EXPORT QUALIFIED etc. Btw I have to say while typealiasing was the perfect solution in old delphi/fpc, inlining and generics (which blur the interface<->implementation distinction) make it more fragile. – Marco van de Voort Mar 27 '13 at 13:21