Jump to content
MarkBlakley

RunAs for running as Admin from non-Admin account

Recommended Posts

MarkBlakley

I need to be able to run some applications and commands as Administrator when running from a non-Admin command window.

 

I realize that this is probably one of the most requested use cases for AutoIt in general, and I have been able to get a few of my specific use cases working, but I have some specific needs that I haven't been able to solve yet.  I'm starting to think I'm running into a limitation of Windows, but I don't know enough to really confirm that.

 

I'd really like to be able to start a non-Admin cmd window and run an application as Admin (using the RunAs function from an AutoIt app), and get both the console output and the exit code back.  It seems like I can do one or the other, but I can't get both when running from a non-Admin console window.  I've tried some UDFs, and made changes based on this process UDF (https://www.autoitscript.com/forum/files/file/356-process-udf/) to allow RunAs, but it doesn't seem to work in this case.

 

Other notes about my setup:

  • UAC is turned all the way down (off)
  • I don't have the ability to manually interact with the system at any point in the process - it's part of a manual reimage install loop for CI automated testing
  • The AutoItRunAs.exe will be called from a VBScript that's running under a non-Admin account
  • Windows 8+ only (mainly Windows 10)

 

Here's a simplified example of the AutoIt code that I have:

#AutoIt3Wrapper_Outfile_x64=AutoItRunAs.exe
#AutoIt3Wrapper_UseX64=y
#AutoIt3Wrapper_Change2CUI=y
#include <ProcessConstants.au3>
#include <AutoItConstants.au3>
#include <ProcessEx.au3>


Local $output = "", $err = "", $cmd=CmdLine[1], $user=CmdLine[2], $pass=CmdLine[3]
$PID = RunAs($user, "", $pass, 4, $cmd, "", @SW_HIDE, $STDERR_MERGED)


; Get the handle of the process
Local $hProcessHandle = _Process_GetHandle($PID)
While ProcessExists($PID)
    ; Capture the output
    $output &= StdoutRead($PID)
    ; Don't kill the CPU
    Sleep(100)
WEnd

; Capture any remaining output
$output &= StdoutRead($PID)
; Get the exit code from the process handle, so that this AutoIt executable can exit with the same exit code
$exitCode = _Process_GetExitCode($hProcessHandle)
; Write the output from running the command back out, so whoever is watching for the RunAs output can consume it
ConsoleWrite($output)

 

I'd like to be able to call my AutoIt application like this:

> AutoItRunAs.exe <cmd> <username> <password>

 

Examples:

(requires Admin permissions - I get the output, which meets my needs.  I don't care about the exit code in this specific case)

> AutoItRunAs.exe "bcdedit" Administrator AdminPass

 

(requires Admin permissions - doesn't currently perform the copy)

> AutoItRunAs.exe "xcopy D:\myfile\test.txt C:\ /Y /A /H /E" Administrator AdminPass

 

(doesn't require Admin permissions - I still don't get the exit code)

> AutoItRunAs.exe "exit /b 7777" Administrator AdminPass

 

If I run the application from a non-Admin console window, the application either doesn't work (the xcopy example), or it doesn't provide the exit code (the exit /b example).

 

Any help is appreciated.

AutoItRunAs.au3

Share this post


Link to post
Share on other sites
MarkBlakley

Doh, I didn't copy quite enough for my cut down version - script should end with "Exit($exitCode)"

AutoItRunAs.au3

Share this post


Link to post
Share on other sites
antmar904

I've used this many times to perform admin tasks when standard users are logged in:

 

Share this post


Link to post
Share on other sites
MarkBlakley

Thanks for the reference.

I'm not having problems executing the commands as admin - I'm able to do that in certain scenarios.  My issue is getting the console output AND exit code back to the calling vbs script.  I want it all!  It seems like the script that you've provided just elevates permissions, which I had previously gotten to work with a different script that didn't try to get the output and exit code.

Using my code above, if I remove the $STDERR_MERGED parameter off of my RunAs call, it will actually successfully run the command as Admin - I just won't get the console output.

Share this post


Link to post
Share on other sites
MarkBlakley

Anyone have any insight into what's happening for my specific case?  I'm assuming it's a limitation of Windows at this point, but I'd really like to know if anyone has any workarounds.

Share this post


Link to post
Share on other sites
jguinch

It works for me  - I made some few changes, just to use the native WinAPIProc.au3 instead of ProcessEx.au3.

The code I used :

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#NoTrayIcon
#include <ProcessConstants.au3>
#include <AutoItConstants.au3>
#include <WinAPIProc.au3>

If $CmdLine[0] <> 3 Then
    ConsoleWrite("Command line error")
    Exit 1
EndIf

$iPID = RunAs($CmdLine[2], "", $CmdLine[3], 1, $CmdLine[1], "", "", $STDERR_MERGED)
If @error Then
    ConsoleWrite("Unable to run the program. Please check the syntax and/or the credentials")
    Exit 1
EndIf

Local $output = "", $err = "", $cmd= $CmdLine[1], $user=$CmdLine[2], $pass=$CmdLine[3]
$iPID = RunAs($user, "", $pass, 4, $cmd, "", @SW_HIDE, $STDERR_MERGED)


Local $output
; Get the handle of the process
Local $hProcess = _WinAPI_OpenProcess($PROCESS_QUERY_LIMITED_INFORMATION, 0, $iPID)
While ProcessExists($iPID)
    ; Capture the output
    $output &= StdoutRead($iPID)
    ; Don't kill the CPU
    Sleep(100)
WEnd
; Capture any remaining output
$output &= StdoutRead($iPID)

; Get the exit code from the process handle, so that this AutoIt executable can exit with the same exit code
$exitCode = _WinAPI_GetExitCodeProcess($hProcess)
; Write the output from running the command back out, so whoever is watching for the RunAs output can consume it
ConsoleWrite($output)
Exit($exitCode)

And the result :

image.png.6b160c99a1d7c6d5d6e8889326e232fb.png

Share this post


Link to post
Share on other sites
MarkBlakley

Thank you jguinch!  I was not aware of the WinAPIProc and WinAPI native includes.

This gets me closer, but not quite there yet.  I'm able to get the exit code and the output when running commands, but I'm still seeing that certain commands are failing to run.  A simple example of something that isn't working is xcopy, even when trying to copy a file to a location that doesn't require Admin privileges. 

> AutoItRunAs.exe "xcopy D:\myfile\test.txt D:\ /Y /A /H /E" Administrator AdminPass

I get an exit code of 0, but I also don't get any console output (normally I would get the file name and "1 file copied" message), and the file doesn't actually get copied.  If I run the xcopy command in a non-Admin command prompt, I get the expected console output and exit code, and the file gets copied correctly.

If I try to use the AutoItRunAs tool to xcopy the file to a location that requires Admin privileges, it behaves the same way as above (no output, exit code 0, file doesn't get copied).  If I run the xcopy command from command line, I get the file name and "Access Denied" written to console output, with an exit code of 4 as expected.

> AutoItRunAs.exe "xcopy D:\myfile\test.txt C:\ /Y /A /H /E" Administrator AdminPass

Maybe it's something different with my environment?  Any chance that you can try it out and see if you get different results?

Share this post


Link to post
Share on other sites
jguinch

After some tests, I see that the xcopy command does not work with either $STDERR_MERGED, $STDERR_CHILD or STDOUT_CHILD. It works with $RUN_CREATE_NEW_CONSOLE but seems to not allow me to retrieve the output stream.

Finally, it seems that there is a issue when I read the xcopy output stream. Try this :

#include <AutoItConstants.au3>

Local $commands = [@ComSpec & " /c  echo test", @ComSpec & " /c xcopy /?"]
For $i = 0 To UBound($commands) - 1
    ConsoleWrite(@CRLF & "# Running command : " & $commands[$i] & @CRLF)
    $pid = Run($commands[$i], "", @SW_HIDE, $STDERR_CHILD + $STDOUT_CHILD )
    ProcessWaitClose($pid)
    ConsoleWrite("output : " & StdoutRead($pid) & "[" & @error & "]" & @CRLF)
Next

The output stream cannot be retrieved with a simple Run(), so no way for RunAs. Maybe there is an option I didn't try, but it seems we cannot do that.

Another way could be to use the output redirection ">". I managed to get it work with it :

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Change2CUI=y
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****
#NoTrayIcon

#include <File.au3>

If $CmdLine[0] <> 3 Then
    ConsoleWrite("Command line error")
    Exit 1
EndIf

Local $sTempFile = _TempFile()

Local $err
Local $cmd = @ComSpec & ' /c ' & $CmdLine[1] & ' > "' & $sTempFile & '"'
Local $user=$CmdLine[2], $pass=$CmdLine[3]

$iExitCode = RunAsWait($user, "", $pass, 4, $cmd, "", @SW_HIDE)
If @error Then
    ConsoleWrite("Unable to run the program. Please check the syntax and/or the credentials")
    Exit 1
EndIf

Local $sOutput = FileRead($sTempFile)
FileDelete($sOutput)
ConsoleWrite($sOutput)
Exit($iExitCode)

 

Share this post


Link to post
Share on other sites
MarkBlakley

Thanks for trying this, and glad that it wasn't just me!

I'm planning to run some more tests to figure out if xcopy is just special, or if it has to do with write permissions when used by any tool (copy, robocopy, etc).  I'll reply with more info after I've had a chance to look at those cases.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×