Jump to content
Sign in to follow this  


Recommended Posts

Ever tried to run as a regular user in Windows XP on a box that you own? You know you should, because it is the more secure way to compute. However, it can be pretty inconvenient. If you need to install software, add a printer, change your network connection, etc, you either need to switch users, Shift-Right click a shortcut to Run As another account, or use the runas command line tool. If you've ever done this, you realize that there are problems with running Administrator functions under another account: changes that affect the profile go to the Administrator account profile, you don't have your normal account's network access, etc.

Aaron Margosis came up with a solution with his MakeMeAdmin batch file. I've extended this concept with my RunAsAdmin AutoIt script. This script prompts for the name and password of an account in the Administrator's group, and for my current account's password. It then runs an Explorer-like window (using the A43 file management utility) under my normal account. However, within this one window, my account has Administrator access. Thus, I have Administrator access, but any changes I make affect my own profile and not the profile of some other account. More details about what it does can be found on my blog.

Here is the script:

; ----------------------------------------------------------------------------
; AutoIt Version: 3.1.0
; Author:       Bluebearr
; Website:      http://www.bluebearr.net
; Copyright 2007 Bluebearr.  Use of this code is governed under the 
; Creative Commons Attribution 3.0 Unported license code (see http://creativecommons.org/licenses/by/3.0/)
; Any use of this script must note "Portions copyright BlueBearr." A link to http://www.bluebearr.net is appreciated.
; Script Function:
;   Add current user to the Administrators group, run a command as an Administrator,
;   then remove the account from the Administrators group
;   Inspired by Aaron Margosis http://blogs.msdn.com/aaron_margosis/archive/2004/07/24/193721.aspx
; ----------------------------------------------------------------------------

; Script Start
#include <GUIConstants.au3>
#include <Constants.au3>
#include <Array.au3>
#Include <process.au3>
#include <File.au3>
#Include <string.au3>

; Dimension global variables
Global $Key, $ScriptNameCore, $AdminDefVal, $WinTitle, $AdminUsersA, $AdminDef, $LocalAdminGroup
Global $AdminAcct, $AdminPass, $CurrentUser, $CurrentPass, $AdminAcctDomain, $CurrentUserDomain

; Main

_Init()     ; Initialize
_CheckAdmin()   ; Check if we are already an Admin. If so, just call _RunAsAdmin
_GetAcct()  ; Prompt user for the Admin account and logon credentials
_MakeAdmin()    ; Add the current account to the local Administrators
_RunAsAdmin()   ; Run the commands under the current account, which is now an Admin account
_DelAdmin()     ; Remove current account from the local Administrators

; =======================
; Functions
; =======================

Func _Init()
    ; Show splash screen, and initialize variables
    Local $TempDir
    ; Show Splash
    $TempDir = _TempFile()
    FileInstall("C:\Program Files\RunAsAdmin\RunAsAdminSplash.jpg", $TempDir & "\splash.jpg", 1)
    SplashImageOn("", $TempDir & "\splash.jpg", 240, 101, -1, -1, 1)
    ; Initialize variables
    $WinTitle = "RunAsAdmin"    ; Title for program windows
    $Key = 'HKCU\Software\Scripts\' & $WinTitle     ; Key where the default account is kept
    $ScriptNameCore = StringLeft(@ScriptName, StringInStr(@ScriptName, ".", 0, -1) - 1)     ; "Core" name of this script,
    ; so we can find files of the same name but different extension
    $AdminDefVal = $ScriptNameCore & "_AdminDef"    ; Value in registry where the default account is kept
    $AdminSID = "S-1-5-32-544" 
    ; Get Administrator accounts and default admin account
    $LocalAdminGroupA = _GetMemberNameFromSID($AdminSID, @ComputerName)     ; Name of the local Administrators group
    If $LocalAdminGroupA[1] = "None"  Then
        MsgBox(0, $WinTitle, "ERROR: Could not find Administrators Group!")
    $LocalAdminGroup = $LocalAdminGroupA[0]
    $AdminDef = RegRead($Key, $AdminDefVal)     ; default admin account to use
    $CurrentUser = @UserName
    $CurrentUserDomain = EnvGet("USERDOMAIN")
    DirRemove($TempDir, 1)
EndFunc   ;==>_Init

Func _CheckAdmin()
    ; Check if we are already an Administrator. If so, just run the command(s).
    If IsAdmin() Then
        SplashTextOn($WinTitle, "Account " & @UserName & " is already an Administrator.", 300, 50, -1, -1, 2, "Tahoma", 10)
EndFunc   ;==>_CheckAdmin

Func _GetAcct()
    ; Show the interface where the user can choose the account, and then enter the passwords.
    ; == GUI generated with Koda ==
    $MMADialog = GUICreate($WinTitle, 316, 260, -1, -1, BitOR($WS_MINIMIZEBOX, $WS_CAPTION, _
    GUICtrlCreateLabel("Adiminstrator account user name:", 16, 8, 161, 17, $WS_GROUP)
    ; Admin accounts
    $AdminAcctEdit = GUICtrlCreateInput($AdminDef, 48, 32, 217, 21)
    $AdminDefaultChk = GUICtrlCreateCheckbox("&Make this account the default.", 48, 64, 177, 17, _
    ; Admin account password
    GUICtrlCreateLabel("Admin account password:", 16, 88, 200, 17, $WS_GROUP)
    $AdminPassEdit = GUICtrlCreateInput("", 48, 112, 217, 21, BitOR($WS_EX_CLIENTEDGE, $ES_PASSWORD))
    If $AdminDef <> "" Then
        GUICtrlSetState(-1, $GUI_FOCUS)
    ; Current account
    GUICtrlCreateLabel("Current account password:", 16, 152, 200, 17)
    GUICtrlCreateLabel("[" & $CurrentUserDomain & "\" & $CurrentUser & "]", 20, 170, 196, 17)
    ;GUICtrlSetFont (-1, 8.5, 400, 2)
    ; Current account password
    $CurrentPassEdit = GUICtrlCreateInput("", 48, 187, 217, 21, BitOR($WS_EX_CLIENTEDGE, $ES_PASSWORD))
    ; Buttons
    $OKBtn = GUICtrlCreateButton("&OK", 200, 220, 73, 25, $BS_DEFPUSHBUTTON)
    $CancelBtn = GUICtrlCreateButton("&Cancel", 32, 220, 73, 25)
    ;$SettingsBtn = GUICtrlCreateButton("&Settings", 112, 256, 89, 25, 0)
    While 1
        $msg = GUIGetMsg()
            Case $msg = $GUI_EVENT_CLOSE
            Case $msg = $OKBtn
                ; Verify that the passwords have been entered
                If GUICtrlRead($AdminPassEdit) = "" Or GUICtrlRead($CurrentPassEdit) = "" Then
                    MsgBox(48, $WinTitle, "Please enter the password for both accounts.")
                    ; Get the selected Admin account
                    $AdminAcct = GUICtrlRead($AdminAcctEdit)
                    $AdminPass = GUICtrlRead($AdminPassEdit)
                    ; Get the current account password
                    $CurrentPass = GUICtrlRead($CurrentPassEdit)
                    ; Save the default account to the registry
                    If BitAND(GUICtrlRead($AdminDefaultChk), $GUI_CHECKED) Then
                        RegWrite($Key, $AdminDefVal, "REG_SZ", $AdminAcct)
            Case $msg = $CancelBtn
            Case Else
    If $AdminAcct = "" Then Exit
EndFunc   ;==>_GetAcct

Func _MakeAdmin()
    ; Make the Current user an Administrator, then the run the command under the elevated account
    ; and then remove the privileges
    ; Get the domain
    If StringInStr($AdminAcct, "\") Then
        $AdminAcctDomain = StringLeft($AdminAcct, StringInStr($AdminAcct, "\") - 1)
        $AdminAcct = StringRight($AdminAcct, StringLen($AdminAcct) - StringInStr($AdminAcct, "\"))
        $AdminAcctDomain = @ComputerName
    ; Make me an Administrator
    SplashTextOn($WinTitle, "Making account " & $CurrentUserDomain & '\' & $CurrentUser & " an Administrator.", _
        300, 50, -1, -1, 2, "Tahoma", 10)
    RunAsSet($AdminAcct, $AdminAcctDomain, $AdminPass)
    RunWait(@ComSpec & ' /c net localgroup ' & $LocalAdminGroup & ' "' & $CurrentUserDomain & '\' & $CurrentUser _
        & '" /ADD', @SystemDir, @SW_HIDE)
    ControlSetText($WinTitle, "Making account ", "Static1", "Launching app...")
    ; Change the RunAsSet here rather than in _RunAsAdmin so that _CheckAdmin can call _RunAsAdmin without any problem
    RunAsSet($CurrentUser, $CurrentUserDomain, $CurrentPass)
EndFunc   ;==>_MakeAdmin

Func _DelAdmin()
    ; Remove the current account from the Administrators group
    RunAsSet($AdminAcct, $AdminAcctDomain, $AdminPass)
    RunWait(@ComSpec & ' /c net localgroup ' & $LocalAdminGroup & ' "' & $CurrentUserDomain & '\' & $CurrentUser _
        & '" /DELETE', @SystemDir, @SW_HIDE)
EndFunc   ;==>_DelAdmin

Func _RunAsAdmin()
    ; Just run the command(s) that you want to run as an Administrator
    ; Look for one of several filenames in the current directory to run
    $AllCmdRuns = @ScriptDir & "\" & $ScriptNameCore & ".bat" & @LF _
             & @ScriptDir & "\" & $ScriptNameCore & ".cmd" & @LF _
             & @ScriptDir & "\A43.exe" 
    $CmdRunA = StringSplit($AllCmdRuns, @LF)
    $CmdRun = "***None***" 
    For $i = $CmdRunA[0] To 1 Step - 1
        If FileExists($CmdRunA[$i]) Then $CmdRun = $CmdRunA[$i]
    If $CmdRun = "***None***"  Then
        ; No external batch files to run, just open command prompt
        Run(@ComSpec & ' /k title *** ' & $CurrentUserDomain & '\' & $CurrentUser & ' as Admin *** & ver & color 4f')
    If StringRight($CmdRun, 7) = "A43.exe"  Then
        $CmdRun = $CmdRun & " C:\" 
        Run($CmdRun, @ScriptDir)
        SplashTextOn($WinTitle, "Starting A43...", 300, 50, -1, -1, 2, "Tahoma", 10)
        WinWaitActive("A43 ->", "", 15)
        Run(@ComSpec & ' /c "' & $CmdRun & '"', @ScriptDir, @SW_MINIMIZE)
EndFunc   ;==>_RunAsAdmin

Func _GetMemberNameFromSID($strSID, $strDomain)
    ; Return the name of a user or group account based on the SID.
    ; Returns a 2 item array. Item 1 is the name, and item 2 is "User", "Group", or "None"
    ; Generated by AutoIt Scriptomatic
    $wbemFlagReturnImmediately = 0x10
    $wbemFlagForwardOnly = 0x20
    $colItems = ""
    $strComputer = $strDomain
    Dim $MemberNameA[2]

    ; Test the group names
    $objWMIService = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
    $colItems = $objWMIService.ExecQuery ("SELECT * FROM Win32_Group", "WQL", _
            $wbemFlagReturnImmediately + $wbemFlagForwardOnly)

    If IsObj($colItems) Then
        For $objItem In $colItems
            $strItemSID = $objItem.SID
            If $strItemSID = $strSID Then
                ; Found a match. Return the name and that it is a group
                $MemberNameA[0] = $objItem.Name
                $MemberNameA[1] = "Group" 
                Return $MemberNameA
    ; Test the User names
    $colItems = $objWMIService.ExecQuery ("SELECT * FROM Win32_UserAccount", "WQL", _
            $wbemFlagReturnImmediately + $wbemFlagForwardOnly)
    If IsObj($colItems) Then
        For $objItem In $colItems
            $strItemSID = $objItem.SID
            If $strItemSID = $strSID Then
                ; Found a match. Return the name and that it is a user
                $MemberNameA[0] = $objItem.Name
                $MemberNameA[1] = "User" 
                Return $MemberNameA
    $MemberNameA[0] = "##ERROR_NOT_FOUND##" 
    $MemberNameA[1] = "None" 
    Return $MemberNameA
EndFunc   ;==>_GetMemberNameFromSID
Edited by bluebearr

BlueBearrOddly enough, this is what I do for fun.

Share this post

Link to post
Share on other sites

It looks so very useful!!!

But how can I find that it really has those admin permissions?

I tried running gpresult from a command line - it didn't show it had admin permissions.

I ran computer management and looked at administrator group and didn't see it either.

But I know it works because that user account couldn't run any au3 scripts from the Scite editor, but if I started it in from the command prompt after I ran a complied version of your script, it worked.

What dos command tells me I have elevated permissions?

Also, I didn't notice any splash screen come up when I ran this. I gave it a .jpg with the name you had and placed it in the folder you show. Is it because it happens so fast?

Thanks very much for this. Great example.

Share this post

Link to post
Share on other sites

Easy. Just build a macro that will change the administrator password.

and then stick it inside of the start up menu for programs start up

all users. And then when any of the administrators log on

it will change the password of the account named

administrator. It's that simple. If you don't know how to do it

respond to my post, with your email. and i will build it

for free.

Share this post

Link to post
Share on other sites

i will buil;d it for you. I will upload it here.

just respond if you want it.

That would be very helpful for everyone if you could do that. Thanks for the help.

Share this post

Link to post
Share on other sites

How is such a script going to be helpful? Let's think about this for about half a second here. The idea is to change the administrator password. That's going to work only if 1) You know the current password; 2) There is no password set. If either of those is the case, what's stopping the person from logging on manually and changing the administrator's password? I also don't see how "a script to change the administrator's password" is at all relevant to the project posted here. This is about intentionally running as a limited user but providing a convenient way of gaining administrator rights for privileged actions. In short, it's a poor man's manual UAC to relate this to Vista's integrated approach.

Share this post

Link to post
Share on other sites

That's going to work only if 1) You know the current password; 2) There is no password set.

Although I agree with the rest of your post, your number is false. The current password does not have to be known to change the password.


Share this post

Link to post
Share on other sites

Commenting on the script from the original poster.

There is a very large security issue with the script. Namely, if anything fails between the time you add the user to the administrators group and the time you delete it from the group, you will leave your account stuck in the administrators group and won't even realize it. The best approach to doing all you can to guarantee proper cleanup is to simply not run the group-add and group-remove stuff in the same process you do everything else. A second instance should do nothing but add the account to the administrators group, wait on some sort of synchronization event then remove the account from the group when the event fires.

Nikolai, I see that you are correct. I realize there needs to be some way of overriding passwords but I never expected it to be so blindingly easy.

At any rate, the script still won't work, at least according to the security tab. Limited users don't have write access to the All Users startup entry.

Share this post

Link to post
Share on other sites

What do I do if neather account has a password. Im not living in a house where I need one.



"If a vegetarian eats vegetables,What the heck does a humanitarian eat?"

"I hear voices in my head, but I ignore them and continue on killing."

"You have forced me to raise the indifference warning to beige, it's a beige alert people. As with all beige alerts please prepare to think about the possibility of caring."

An optimist says that giving someone power DOESN'T immediately turn them into a sadist. A pessimist says that giving someone power doesn't IMMEDIATELY turn them into a sadist.



Share this post

Link to post
Share on other sites

Sorry, been away for a while.

@ draygoes : I haven't tested it, but I believe that the script requires that the Administrator account have a password, since runas won't work with an account with a blank password.

@ Valik : Thanks for your evaluation. You are right, there is the possibility that the user could remain in the Administrators group. As far as I can tell, the "group-add process" and the "launching-as-an-Admin process" steps have to happen in the same process. This is because other processes won't "see" that the account is a member of the Administrator's group. So, if the group-add step occurs in a second process, the first process won't be able to run a command as an Administrator because it will appear to it that the user is not a member of the Administrators group.

However, the idea of having a "watchdog" process that focuses on locking down the group access has merit. It might be good to have the _MakeAdmin() function launch a second process under the Administrator account that would wait (with timeout) for the synchronization event you mention and then remove the user from the Administrators group. I did a test, and it looks like this could work, i.e., the second process "sees" that the user is in the Administrators group.

It would also help in my current script if I moved the $CmdRun evaluation that occurs in the _RunAsAdmin() function to some other place earlier in the script. This would reduce the number of steps between the group-add and group-remove events (and thus reduce the possibility of the script hanging between these two events.

Thanks again.

BlueBearrOddly enough, this is what I do for fun.

Share this post

Link to post
Share on other sites

hi, I'd like to put some modifications on your script. Do you give me the right to do it and publish the new code on this forum?

Share this post

Link to post
Share on other sites

thank pro.

i have a question. I want to runas a program with another user, can you help me? thank!

hi, for start a program with another user, you must use the command "RunAs" or "RunAsWait".

RunAs ( "username", "domain", "password", logon_flag, "filename" [, "workingdir" [, show_flag [, opt_flag ]]] )

RunAsWait ( "username", "domain", "password", logon_flag, "filename" [, "workingdir" [, show_flag [, opt_flag ]]] )

for more informations read the help of AutoIt "F1".

bye, Asilus

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
Sign in to follow this  

  • Create New...