News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Run Elevated

Started by GregL, June 24, 2008, 11:25:15 PM

Previous topic - Next topic

GregL

This program will bring up a UAC prompt to run the specified program elevated in Windows Vista. It will bring up a 'Run As' dialog in Windows XP SP3. I'm not sure what it does in any other version of Windows.


; Link with /SUBSYSTEM:WINDOWS
.686
.MODEL FLAT,STDCALL
OPTION CASEMAP:NONE

INCLUDE windows.inc

INCLUDE kernel32.inc
INCLUDE user32.inc
INCLUDE msvcrt.inc
INCLUDE shell32.inc
INCLUDE masm32.inc

INCLUDE c:\masm32\macros\macros.asm

INCLUDELIB kernel32.lib
INCLUDELIB user32.lib
INCLUDELIB msvcrt.lib
INCLUDELIB shell32.lib
INCLUDELIB masm32.lib

RunElevated PROTO :DWORD,:PTR BYTE,:PTR BYTE
GetErrorMessage PROTO :DWORD,:PTR BYTE,:DWORD

.CODE

  start:

    call main
    INVOKE ExitProcess, 0

main PROC

    LOCAL err:DWORD
    LOCAL szBuffer[256]:BYTE
   
    INVOKE RunElevated, NULL, SADD("regedit.exe"), NULL
    .IF eax == FALSE
        mov err, edx
        INVOKE GetErrorMessage, err, ADDR szBuffer, SIZEOF szBuffer
        INVOKE MessageBox, NULL, ADDR szBuffer, SADD("RunElevated"), MB_ICONERROR
    .ENDIF   
    ret
main ENDP
;---------------------------------------
RunElevated PROC hWnd:DWORD, pFilename:PTR BYTE, pParameters:PTR BYTE
    ; Requests the OS to run the executable elevated.
    ; Returns TRUE if successful, or FALSE otherwise.
    ; If FALSE then return error information in edx
   
    LOCAL sei:SHELLEXECUTEINFO
   
    INVOKE RtlZeroMemory, ADDR sei, SIZEOF sei
    mov sei.cbSize, SIZEOF SHELLEXECUTEINFO
    mov eax, hWnd
    mov sei.hwnd, eax
    mov sei.fMask, SEE_MASK_FLAG_DDEWAIT OR SEE_MASK_FLAG_NO_UI
    mov eax, CTXT("runas")
    mov sei.lpVerb, eax
    mov eax, pFilename
    mov sei.lpFile, eax
    mov eax, pParameters
    mov sei.lpParameters, eax
    mov sei.nShow, SW_SHOWNORMAL
    INVOKE ShellExecuteEx, ADDR sei
    .IF eax == FALSE
        INVOKE GetLastError
        mov edx, eax
        xor eax, eax
    .ELSE   
        mov eax, 1
    .ENDIF   
    ret
RunElevated ENDP
;---------------------------------------
GetErrorMessage PROC dwErrorNum:DWORD, pBuffer:PTR BYTE, dwBufferLen:DWORD
   
    LOCAL hModule    :DWORD
    LOCAL dwDefLangID:DWORD
    LOCAL dwFlags    :DWORD
    LOCAL retv       :SDWORD
    LOCAL pTmpBuffer :PTR BYTE
   
    mov hModule, NULL
    mov pTmpBuffer, NULL
    INVOKE GetUserDefaultLangID
    mov dwDefLangID, eax
    mov eax, FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS
    mov dwFlags, eax   
    .IF dwErrorNum >= NERR_BASE && dwErrorNum <= MAX_NERR
        INVOKE LoadLibraryEx, SADD("netmsg.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE
        mov hModule, eax
        .IF hModule != NULL
            mov eax, dwFlags
            or  eax, FORMAT_MESSAGE_FROM_HMODULE
            mov dwFlags, eax
        .ENDIF
    .ENDIF
    INVOKE FormatMessage, dwFlags, hModule, dwErrorNum, dwDefLangID, ADDR pTmpBuffer, 0, NULL
    .IF eax                         
        dec dwBufferLen
        INVOKE lstrcpyn, pBuffer, pTmpBuffer, dwBufferLen
        INVOKE LocalFree, pTmpBuffer
        mov retv, 0
    .ELSE
        INVOKE GetLastError
        mov retv, eax
    .ENDIF 
    .IF hModule != NULL
        INVOKE FreeLibrary, hModule
    .ENDIF   
    mov eax, retv
    ret                         
GetErrorMessage ENDP   
;---------------------------------------
END start


[edit] Updated GetErrorMessage (uses lstrcpyn instead of lstrcpy)




GregL

This one is a little more useful, accepts command line arguments. For Windows Vista.


; Link with /SUBSYSTEM:WINDOWS
.686
.MODEL FLAT,STDCALL
OPTION CASEMAP:NONE

INCLUDE windows.inc

INCLUDE kernel32.inc
INCLUDE user32.inc
INCLUDE msvcrt.inc
INCLUDE shell32.inc
INCLUDE masm32.inc

INCLUDE c:\masm32\macros\macros.asm

INCLUDELIB kernel32.lib
INCLUDELIB user32.lib
INCLUDELIB msvcrt.lib
INCLUDELIB shell32.lib
INCLUDELIB masm32.lib

GetCommandLineItemsCount PROTO
GetCommandLineItem PROTO :DWORD
RunElevated PROTO :DWORD,:PTR BYTE,:PTR BYTE
GetErrorMessage PROTO :DWORD,:PTR BYTE,:DWORD

.CODE

start:

    call main
    INVOKE ExitProcess, 0

;---------------------------------------
main PROC

    LOCAL pszCommand1:PTR BYTE
    LOCAL pszCommand2:PTR BYTE
    LOCAL err:DWORD
    LOCAL szBuffer[256]:BYTE

    INVOKE GetCommandLineItemsCount
    .IF eax == 2
        INVOKE GetCommandLineItem, 1    ; get the first argument
        mov pszCommand1, eax
        INVOKE lstrlen, pszCommand1
        .IF eax > 0
            INVOKE RunElevated, NULL, pszCommand1, NULL
            .IF eax == FALSE
                mov err, edx
                INVOKE GetErrorMessage, err, ADDR szBuffer, SIZEOF szBuffer
                INVOKE MessageBox, NULL, ADDR szBuffer, SADD("RunElevated"), MB_ICONERROR
            .ENDIF   
        .ENDIF
    .ELSEIF eax == 3
        INVOKE GetCommandLineItem, 1    ; get the first argument
        mov pszCommand1, eax
        INVOKE GetCommandLineItem, 2    ; get the second argument
        mov pszCommand2, eax
        INVOKE lstrlen, pszCommand1
        .IF eax > 0
            INVOKE RunElevated, NULL, pszCommand1, pszCommand2
            .IF eax == FALSE
                mov err, edx
                INVOKE GetErrorMessage, err, ADDR szBuffer, SIZEOF szBuffer
                INVOKE MessageBox, NULL, ADDR szBuffer, SADD("RunElevated"), MB_ICONERROR
            .ENDIF   
        .ENDIF       
    .ELSE   
        INVOKE MessageBox, NULL, SADD("Usage: RunElevated exepath [arg]"), SADD("RunElevated"), MB_ICONINFORMATION
    .ENDIF
    ret

main ENDP
;---------------------------------------
GetCommandLineItemsCount PROC

    ; Returns the number of individual command-line arguments in EAX
    ; Will always return at least 1
    ; eax = 1 = 1 item  = name of current exe (item 0)
    ; eax = 2 = 2 items = name of current exe (item 0) + first argument (item 1)
    ; eax = 3 = 3 items = name of current exe (item 0) + first argument (item 1) + second argument (item 2)
    ; ...

    LOCAL dwArgC:DWORD
    LOCAL pArgV:PTR DWORD
    LOCAL pEnv:PTR DWORD
    LOCAL bWildCard:DWORD
    LOCAL StartInfo:STARTUPINFO
   
    mov bWildCard, FALSE
    INVOKE crt___getmainargs, ADDR dwArgC, ADDR pArgV, ADDR pEnv, bWildCard, ADDR StartInfo
    ; __getmainargs
    ; pArgV is a pointer to an array of pointers.
    ;   Each pointer points to an individual command-line argument.
    ; dwArgC is the number of individual command-line arguments.
    ; pEnv is a pointer to an array of pointers.
    ;   Each pointer points to an individual environment variable.
    ;   A NULL pointer signals the end of the array of pointers.
    ; bWildCard is a boolean value controlling whether the command line
    ;   will be globbed (e.g. *.* expanded to be all files in the startup
    ;   directory).
    ; StartInfo is a pointer to a STARTUPINFO structure.
    mov eax, dwArgC
    ret
GetCommandLineItemsCount ENDP
;---------------------------------------
GetCommandLineItem PROC dwItem:DWORD

    ; dwItem = 0 = name of current exe
    ; dwItem = 1 = first argument
    ; dwItem = 2 = second argument
    ; ...
   
    ; Returns a pointer to the command-line item.
    ; NULL otherwise.

    LOCAL dwArgC:DWORD
    LOCAL pArgV:PTR DWORD
    LOCAL pEnv:PTR DWORD
    LOCAL bWildCard:DWORD
    LOCAL StartInfo:STARTUPINFO
   
    mov bWildCard, FALSE
    INVOKE crt___getmainargs, ADDR dwArgC, ADDR pArgV, ADDR pEnv, bWildCard, ADDR StartInfo
    ; __getmainargs
    ; pArgV is a pointer to an array of pointers.
    ;   Each pointer points to an individual command-line argument.
    ; dwArgC is the number of individual command-line arguments.
    ; pEnv is a pointer to an array of pointers.
    ;   Each pointer points to an individual environment variable.
    ;   A NULL pointer signals the end of the array of pointers.
    ; bWildCard is a boolean value controlling whether the command line
    ;   will be globbed (e.g. *.* expanded to be all files in the startup
    ;   directory).
    ; StartInfo is a pointer to a STARTUPINFO structure.
    mov eax, dwItem
    .IF eax >= dwArgC
         xor eax, eax
    .ELSE
        mov edx, pArgV
        mov ecx, dwItem
        mov eax, [edx+ecx*4]
   .ENDIF   
    ret
GetCommandLineItem ENDP
;---------------------------------------
RunElevated PROC hWnd:DWORD, pFilename:PTR BYTE, pParameters:PTR BYTE
    ; Requests the OS to run the executable elevated.
    ; Returns TRUE if successful, or FALSE otherwise.
    ; If FALSE then return error information in edx
   
    LOCAL sei:SHELLEXECUTEINFO
   
    INVOKE RtlZeroMemory, ADDR sei, SIZEOF sei
    mov sei.cbSize, SIZEOF SHELLEXECUTEINFO
    mov eax, hWnd
    mov sei.hwnd, eax
    mov sei.fMask, SEE_MASK_FLAG_DDEWAIT OR SEE_MASK_FLAG_NO_UI
    mov eax, CTXT("runas")
    mov sei.lpVerb, eax
    mov eax, pFilename
    mov sei.lpFile, eax
    mov eax, pParameters
    mov sei.lpParameters, eax
    mov sei.nShow, SW_SHOWNORMAL
    INVOKE ShellExecuteEx, ADDR sei
    .IF eax == FALSE
        INVOKE GetLastError
        mov edx, eax
        xor eax, eax
    .ELSE   
        mov eax, 1
    .ENDIF   
    ret
RunElevated ENDP
;---------------------------------------
GetErrorMessage PROC dwErrorNum:DWORD, pBuffer:PTR BYTE, dwBufferLen:DWORD
   
    LOCAL hModule    :DWORD
    LOCAL dwDefLangID:DWORD
    LOCAL dwFlags    :DWORD
    LOCAL retv       :SDWORD
    LOCAL pTmpBuffer :PTR BYTE
   
    mov hModule, NULL
    mov pTmpBuffer, NULL
    INVOKE GetUserDefaultLangID
    mov dwDefLangID, eax
    mov eax, FORMAT_MESSAGE_ALLOCATE_BUFFER OR FORMAT_MESSAGE_FROM_SYSTEM OR FORMAT_MESSAGE_IGNORE_INSERTS
    mov dwFlags, eax   
    .IF dwErrorNum >= NERR_BASE && dwErrorNum <= MAX_NERR
        INVOKE LoadLibraryEx, SADD("netmsg.dll"), NULL, LOAD_LIBRARY_AS_DATAFILE
        mov hModule, eax
        .IF hModule != NULL
            mov eax, dwFlags
            or  eax, FORMAT_MESSAGE_FROM_HMODULE
            mov dwFlags, eax
        .ENDIF
    .ENDIF
    INVOKE FormatMessage, dwFlags, hModule, dwErrorNum, dwDefLangID, ADDR pTmpBuffer, 0, NULL
    .IF eax                         
        dec dwBufferLen
        INVOKE lstrcpyn, pBuffer, pTmpBuffer, dwBufferLen
        INVOKE LocalFree, pTmpBuffer
        mov retv, 0
    .ELSE
        INVOKE GetLastError
        mov retv, eax
    .ENDIF 
    .IF hModule != NULL
        INVOKE FreeLibrary, hModule
    .ENDIF   
    mov eax, retv
    ret                         
GetErrorMessage ENDP   
;---------------------------------------
END start


sinsi

Any reason for using crt___getmainargs instead of CommandLineToArgvW ?
Light travels faster than sound, that's why some people seem bright until you hear them.

GregL

I found __getmainargs to be pretty easy to use. Why not?


hutch--

Greg,

Make me a little wiser here, what is the sense of "elevated" in Vista ? Is it something like running a process with its priority set higher ?
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

sinsi

Quote from: Greg on June 25, 2008, 04:21:06 AM
I found __getmainargs to be pretty easy to use. Why not?
Just curious. It seems to need a bit more (in the way of vars), but I guess does a bit more too.
Are you a C programmer? Since I'm not, my first choice would be CommandLineToArgvW (or a roll-your-own version for win9x).

hutch, elevated means (usually) "run as administrator" or at least brings up the UAC dialog asking permission to run at a higher user access (I think).
greg, does your code bring up the dialog, or bypass it? (not having vista, it's hard to check).[edit]Sorry, read your 1st post properly - is there any way of bypassing the dialog?[/edit]
Light travels faster than sound, that's why some people seem bright until you hear them.

GregL

#6
sinsi,

Yes, I have a background in C programming. Just trying out something different. Yes, it does bring up the UAC dialog, and no, there is not a way to bypass the UAC dialog.   

hutch,

In Windows Vista your user account does not automatically have administrator privileges. Running a program 'elevated' means running with administrator privileges.

This is a very good article about it Inside Windows Vista User Account Control

I am currently finishing up a program that demonstrates how a program can request elevation at startup. It's done with a manifest element. I'll post it when it's ready. 


GregL

The attached program is an example intended for Windows Vista. It demonstrates how a program can request elevation at start up. Notice the manifest element about "requireAdministrator". It also shows how to add an 'elevation required' shield to a button.



[attachment deleted by admin]

farrier

Greg,

Thanks for that link!  I also found the following link:

http://social.msdn.microsoft.com/forums/en-US/windowsgeneraldevelopmentissues/thread/8f19b0ba-cb14-4081-8622-c3b3a6481e48/

helpful with a problem I had with a program which required mouse control of a separate program.  It describes how to add the uiAccess parameter to the manifest.  Which also required adding "a valid, trusted digital signature to execute" see post by Jimmy Brush.  It worked for me in fasm.

Thanks again,

farrier
It is a GOOD day to code!
Some assembly required!
ASM me!
With every mistake, we must surely be learning. (George...Bush)

oex

I read somewhere I think that running as administrator also opens up security holes ie if your app opens browser then browser is also running as administrator (may be bad example but I think you will get the drift)
We are all of us insane, just to varying degrees and intelligently balanced through networking

http://www.hereford.tv

donkey

Hi Greg,

Just curious, why the SEE_MASK_FLAG_DDEWAIT flag ? It is deprecated and since it instructs Windows to complete any outstanding DDE conversations before allowing the program to terminate it was normally only used with programs that exited very quickly.

Edgar
"Ahhh, what an awful dream. Ones and zeroes everywhere...[shudder] and I thought I saw a two." -- Bender
"It was just a dream, Bender. There's no such thing as two". -- Fry
-- Futurama

Donkey's Stable

GregL

I  don't know Donkey, it was close to two years ago when I did this.  I see it is deprecated, when was it deprecated?  It says to use use SEE_MASK_NOASYNC instead, notice the numbers (0x00000100) are the same.  I'm sure I got it from an example (probably Microsoft) that I was looking at at that time.

Quote from: MSDN SHELLEXECUTEINFO

SEE_MASK_FLAG_DDEWAIT (0x00000100)

    0x00000100. Do not use; use SEE_MASK_NOASYNC instead.

SEE_MASK_NOASYNC (0x00000100)

    0x00000100. Wait for the execute operation to complete before returning. This flag should be used by callers that are using ShellExecute forms that might result in an async activation, for example DDE, and create a process that might be run on a background thread. (Note: ShellExecuteEx runs on a background thread by default if the caller's threading model is not Apartment.) Calls to ShellExecuteEx from processes already running on background threads should always pass this flag. Also, applications that exit immediately after calling ShellExecuteEx should specify this flag.

    If the execute operation is performed on a background thread and the caller did not specify the SEE_MASK_ASYNCOK flag, then the calling thread waits until the new process has started before returning. This typically means that either CreateProcess has been called, the DDE communication has completed, or that the custom execution delegate has notified ShellExecuteEx that it is done. If the SEE_MASK_WAITFORINPUTIDLE flag is specified, then ShellExecuteEx calls WaitForInputIdle and waits for the new process to idle before returning, with a maximum timeout of 1 minute.

    For further discussion on when this flag is necessary, see the Remarks section.

Remarks

The SEE_MASK_NOASYNC flag must be specified if the thread calling ShellExecuteEx does not have a message loop or if the thread or process will terminate soon after ShellExecuteEx returns. Under such conditions, the calling thread will not be available to complete the DDE conversation, so it is important that ShellExecuteEx complete the conversation before returning control to the calling application. Failure to complete the conversation can result in an unsuccessful launch of the document.

If the calling thread has a message loop and will exist for some time after the call to ShellExecuteEx returns, the SEE_MASK_NOASYNC flag is optional. If the flag is omitted, the calling thread's message pump will be used to complete the DDE conversation. The calling application regains control sooner, since the DDE conversation can be completed in the background.







sinsi

MSDN online says that, but the 2003 SDK I have doesn't even mention SEE_MASK_NOASYNC.
Online also says the minimum required client for ShellExecuteEx is XP, whereas the SDK says Windows 95/NT 3.51  ::)
Light travels faster than sound, that's why some people seem bright until you hear them.

dedndave

QuoteOnline also says the minimum required client for ShellExecuteEx is XP, whereas the SDK says Windows 95/NT 3.51

there are a lot of these what seem to be discrepencies
i think they specify only the OS level that behaves as the document specifies
older OS's may well support the function, but enough of the behaviour has changed that they are not included
at least, that is how it appears to me
of course - they quit supporting win9x long ago - that may have something to do with it   :P

oex

As I have understood it it lists the earliest *supported* compatible version online
We are all of us insane, just to varying degrees and intelligently balanced through networking

http://www.hereford.tv