Running tcl 8.4.13, I have a tcl script to execute my NSIS installation application (duly signed by microsoft) that used to work but now fails (windows 7 and 10), maybe because of windows security update or something? The same tcl script works fine when the target is a local/renamed copy of notepad.exe. The NSIS application works fine when run from from the command line.
The tcl script looks like the following, run via tclkit-win32 tclmnu.tcl, where tclmnu.tcl looks like this:
#! /bin/sh -x
# \
exec wish "$0" "$#"
#
package require Tk
#set runcmd notepad_local_copy.exe
set runcmd my_nsis_app.exe # this doesn't work
# this works with the notepad_local_copy.exe (above) but not my_nsis_app.exe
set catchcode [ catch { exec ${runcmd} } result ]
# also tried this, doesn't work either
#set catchcode [ catch { exec "runas /usr:administrator: ${runcmd}" } result ]
tk_messageBox -type ok -icon error -message "DEBUG: catchcode=${catchcode}"
# catchcode is 0 when runas=notepad_local_copy.exe, 1 when it's my_nsis_app.exe
Well, I solved it by adding cmd /c, as in
set catchcode [ catch { cmd /c exec ${runcmd} } result ]
I don't know why it needs that, nor why it seemed to work before and not now.
I don't know anything about TCL but I'm assuming that exec calls CreateProcess. When UAC was added to Vista it seems Microsoft deemed CreateProcess too low-level to be able to perform elevation.
Dealing with Administrator and standard user’s context:
CreateProcess and CreateProcessWithLogonW do not have new flags to launch the child process as elevated. Internally, CreateProcess() checks whether the target application requires elevation by looking for a manifest, determining if it is an installer, or if it has an app compat shim. If CreateProcess() determines the target application requires elevation, it simply fails with ERROR_ELEVATION_REQUIRED(740). It will not contact the AIS to perform the elevation prompt or run the app. If CreateProcess() determines the application doesn’t require elevation, it will spawn it as a new process.
ShellExecute[Ex] however is able to contact the UAC service and display the UAC elevation dialog:
To programmatically launch a child process as an elevated process, two things must occur: first, the executable of the child process needs to be identified as needing elevation, and second, the parent process needs to use ShellExecute() or ShellExecuteEx().
It seems there are multiple ways to get TCL to call ShellExecute:
eval exec [auto_execok start] {""} [list [file nativename $filename]] ;# http://wiki.tcl.tk/2809
or
twapi::shell_execute -path filename.exe ;# http://www.magicsplat.com/blog/how-do-i-exec-thee/
You can also request elevation of executables even if they are not marked as requiring elevation:
twapi::shell_execute -path filename.exe -verb runas
(The runas verb is not the same thing as runas.exe)
NSIS scripts use the RequestExecutionLevel attribute to mark the executable with the desired UAC elevation level.
Using cmd /c is a bit of a hack and will probably display a console window for a brief period of time. cmd /c first tries CreateProcess and then falls back to ShellExecuteEx if elevation was required. I'm not sure if this is documented behavior.
Related
There are a few options for standard user to run as Administrator (or any another user), however, even when logged as Administrator, some functions requires to run 'elevated'.
On a windows gui, just right click a .exe and select run as Administrator or even elevate 'cmd' or 'powershell'.
How can you get elevated privileges on Windows core?
Generally, to programmatically invoke an executable with elevation (Run as Administrator) on Windows, use the Start-Process cmdlet with -Verb RunAs.
This applies equally to pwsh.exe, the PowerShell Core executable, so that in the simplest case you can write:
# Open a new console window with PowerShell Core running with admin privileges.
Start-Process -Verb RunAs pwsh
If you wanted to wrap that in a convenience function that is also more robust and cross-edition on Windows (also works in Windows PowerShell):
Note: See the bottom section for a more sophisticated function, downloadable from a Gist, which notably also allows passing commands to execute in the elevated PowerShell session.
function Enter-AdminPSSession {
Start-Process -Verb RunAs (Get-Process -Id $PID).Path
}
# Optionally also define a short alias name:
# Note: 'psa' is a nonstandard alias name; a more conformant name would be
# the somewhat clunky 'etasn'
# ('et' for 'Enter', 'a' for admin, and 'sn'` for session), analogous
# to built-in 'etsn' alias referring to 'Enter-PSSession'
Set-Alias psa Enter-AdminPSSession
If you want the function to also be cross-platform (to also work on Unix-like platforms):
function Enter-AdminPSSession {
if ($env:OS -eq 'Windows_NT') {
Start-Process -Verb RunAs (Get-Process -Id $PID).Path
} else {
sudo (Get-Process -Id $PID).Path
}
}
Important: Due to the cmdlets / utilities involved,
on Windows, the new session invariably opens in a new console window.
The fact that the new session is an admin session is reflected in its window's title (prefix Administrator: )
on Unix (Linux, macOS), the new session invariably opens in the same console (terminal) window.
On Unix there is no obvious indicator that an admin session has been entered; running whoami is a quick way to test for that (returns root in an admin session); a better solution would be to modify the prompt function to reflect an admin session in the prompt string, as the prepackage solution discussed next does.
If you additionally want the ability to run commands in the new session and optionally auto-close it, much more work is needed:
You can download function Enter-AdminPSSession from this Gist, which:
enables passing commands to execute via a script block ({ ... })
keeps the session open by default, so that command output can be inspected, but you can opt-out with -Exit or -ExitOnSuccess (close the session only if no error occurred).
tries to reflect overall success of the commands passed via $LASTEXITCODE (even for PowerShell-native commands this variable is normally not set); 0 indicates success.
ensures that the calling session's current location (working directory) is also the elevated session's current location.
allows you to opt out of loading the profiles, with -NoProfile
prefixes the prompt string in interactive elevated sessions with [admin] , on all platforms.
Assuming you have looked at the linked Gist's source code to ensure that it is safe (which I can personally assure you of, but you should always check), you can install Enter-AdminPSSession directly as follows:
irm https://gist.github.com/mklement0/f726dee9f0d3d444bf58cb81fda57884/raw/Enter-AdminPSSession.ps1 | iex
Example calls (which assume that Set-Alias psa Enter-AdminPSSession has been called):
Enter an interactive elevated session:
psa
Windows: Enter an elevated session without loading profiles and set the all-users execution policy, then exit if that succeeded.
psa -NoProfile -ExitOnSuccess { Set-ExecutionPolicy -Scope LocalMachine RemoteSigned }
Unix: Gets the content of file /etc/sudoers (which can only be read with administrative privileges), then exits:
psa -Exit { Get-Content /etc/sudoers }
I have created a Powershell script that I call from a batch file, and everything works fine when I call the batch file. The problem I am running into is I need to set the batch file to run in Task Scheduler. It starts fine, but it keeps hanging up because the task scheduler never says "The operation completed successfully" (0x0). Instead, it stays at "The task is currently running" (0x41301). Please advise, and I understand this is not the most ideal way to call a Powershell Script but for our environment and limited knowledge of scripting it works the best for us.
You should just use Task Scheduler to run PowerShell.
Create a new task, go to Actions tab, then choose New..., and inside this new window, you can run any program, like you run something from cmd.
Inside Program/script square, you simply put Powershell.exe, and inside Add arguments (optional) powershell arguments. This will work the same, as you would type in normal command line:
powershell <arguments>
So if you want to run script, that is saved in your disk, simply put this in arguments list:
C:\LocalisationOfScript\script.ps1 "argument 1" argument2
If you want more options, just add common parameters before this:
-windowstyle hidden -executionpolicy bypass C:\LocalisationOfScript\script.ps1 "argument 1" argument2
Of even this:
-windowstyle hidden -executionpolicy bypass if (Test-path C:\script\script.ps1) { C:\script\script.ps1 "argument 1" argument2 } else { return -1 }
And finally:
Start-Process Powershell.exe -argumentslist "-a -b -c copy" -windowstyle hidden -wait -erroraction stop
You can even add try, catch to last example.
Thank you for all the comments, i researched you advice and came across the exit command i forgot to add to the end of my script, so when i call my script it left the session to exchange open after i applied the exit command to the end of the script the program has been running without error (knock on wood) sense the fix and after i closed and reopened Task Scheduler the last run message changed to (0x0)
I have 2 displays attached to my PC (one is my TV) running Windows 7 and I want to switch between them using a script. I know about the "displayswitch.exe" and its parameters (like /clone, /internal, etc). However, I need the script to work while the PC is locked.
Pressing Windows+P works fine, while the system is locked, which also invokes displayswitch. However running a batch script with "displayswitch.exe /clone" does not work while the PC is locked.
To execute the script, I want to use the Remote Launcher Application on my phone. The Remote Launcher works just fine with a script to shutdown my PC while it is locked, so it is in general able to execute scripts on the locked machine.
Is there any other way, to switch between my displays while the system is locked?
That's not a trivial task.
What displayswitch internally does is to call SetDisplayConfig function. this function must be invoked from a process which lives in a interactive console, otherwise it will return ERROR_ACCESS_DENIED
One may think that he can then invoke displayswitch from psexec using the -i option and indicating the currently active user session; usually powershell
(ps winlogon).si
returns all the interactive user sessions
but launched from psexec, displayswitch.exe doesn't works anyway.
I suppose it is because in any case a command line application doesn't need the "graphical" infrastructure to run, and maybe some internal optimization happens in psexec to save to create a proper graphical environment (at least it doesn't work on my machine, i had no luck with the -x option either)
What you can do it is to write a very simple windows form program, it doesn't even need to create the actual form, it could simply invoke SetDisplayConfig and die. But being a windows form application magically do the trick.
This way you can create a script that find the currently active interactive console id, and then use psexec like this (assuming 1 is the id of the interactive session)
psexec -accepteula -nobanner -i 1 C:\path-to-your-exe\your-exe.exe
i had a powershell module loaded in my profile which i can call it from ssh or any remote connection and it switch my display even if my session is locked, or even if there is no user session logged at all.
obviously the user who runs the script must have the required grant to run psexec -i (mine is machine administrator, so it works, but i don't know which exact grant is required, you can create a functional machine administrator and invoke psexec passing these credential with -u -p parameters)
Try this:
create a job in your windows scheduler executing the desired command (linke "displayswitch.exe /clone")
set a user that has the permission to perform this command and save the password inside your new job
don't set a trigger for the job but enable the option to start it manually
use schtasks /Run /S system /U username /P password /TN taskname to trigger the job
This should execute the desired command no matter in which state your windows is as long as it is running and has network connection.
I have a batch file to start an application as a Windows service. It is called start.bat
#ECHO off
START c:\Ruby193\bin\ruby c:\Ruby193\bin\thin start -R c:\coolapp\config.ru -p 4321 -a localhost -e production
My challenge is that this program only runs properly if it is "Run as Administrator" with admin privileges. So, I would like to add a line to check if this script is actually run with administrative privileges, and only execute if it is being run as administrator.
How can I do that from within the script?
Something like this might be what you need:
set isadmin=0
whoami /all | findstr /c:" S-1-16-12288 ">nul && set isadmin=1
That should result in the %isadmin% variable being either 1 or 0 depending on whether the shell was run as administrator or not.
This assumes the existance of the whoami utility which won't necessarily be available on older versions of Windows - I believe it was included from Windows Vista onwards though.
Two options:
Provoke elevation from a WSH script, like documented in the blog post Scripting Elevation on Vista.
Use an external executable that provokes the UAC prompt, such as Elevate32.exe/Elevate64.exe.
For your scenario, #2 may be preferable because you can detect whether the elevation prompt was canceled (exit code 1223) and you can also wait for the launched executable to finish before continuing (-w parameter).
Bill
It would probably be easier to convert the script to VBScript, then you can more easily check for Admin privileges and even elevate the script to Admin.
See here for how to do the check in VBScript: VBScript: Check if the script has administrative permissions
I have to create a script which updates a system environment variable (based on a command line parameter) before launching a program.
In Windows 7, updating the system environment variable is denied. I would like to perform a privilege elevation for just the setting of the env. var. But run the program as a normal user.
How to do it?
Note:
I've tried the following solution:
Using 2 scripts:
1 master which get all information from command line, which call the slave script to change the system env. var., and which finally launch the program
1 slave script that update the system env. var.
the master script tries to call the slave script using privilege elevation, but that does not work
I've try 2 solutions for the privilage elevation:
Using the "runas /User:Administrator ..." command but it ask for the Administrator password: Fail
Using the "ShellExecute ...., "runas"" command but it tells me that my script is not an application: Fail
I found a way that is working at least on Windows 7 (don't know if it will work on the few Windows XP hat we still have around).
I did the following from the main script:
currentDirectory = left(WScript.ScriptFullName,(Len(WScript.ScriptFullName))-(len(WScript.ScriptName)))
Set UAC = WScript.CreateObject("Shell.Application")
UAC.ShellExecute "wscript.exe", currentDirectory + "my-script.vbs /Param1:Value1 ...", "", "runas", 0
And the my-script is doing the sys var env update.
Note: My fist experience with ShellExecute failed because I was trying to execute the script. Instead of "wscript.exe" I had "my-script.vbs" for the executable name.
IMHO, disable UAC, it's just a pain in the *
But if you can't (like me 8<), you can use
psexec.exe -d -u userid -p password CMD /c program_with_path
You (or the user where the sript runs) will have to confirm the prompt though.