Visar inlägg med etikett AutoIt. Visa alla inlägg
Visar inlägg med etikett AutoIt. Visa alla inlägg

tisdag 10 oktober 2017

The requested execution level require administrator!

Executables that make changes to the system requires higher privilegies. Users will normally be asked to give admin credentials, and even members of the local administrator group will be asked for consent before elevating to a higher level of privilegies.

This is great!

However, some applikations require administrative privilegies even though they do NOT make or need to make any system changes.

That is retarded!

It actually lowers the overall security because if you don't know how to handle it users will have to be given, or some users might even have the balls to demand, administration rights.

An hilariously ironic example is InstallShield, one of the most common msi-editors. I can imagine some tools like the Response Transform Wizard might need admin rights, but otherwise there is no need. Another trouble maker is mmc.exe even though the snap-in you wan't does not. This makes it difficult to save a folder view and give to users to administer groups, for instance. Add to this various third party programs that only need to run as admin because the developer is lazy. Nothing wrong with laziness, unless it gives ME more work.

A program might ask for elevation for various reasons, where the correct and common reason is requestedExecutionLevel in the programs manifest is set to Level=requireAdministrator.

There are other reasons, some of which you can force. You can make a shortcut where you specify Advanced - Run As Administrator. This setting is in the lnk-file and will remain if copied. You can set the executable compability mode to Run As Administrator. This is stored in HKCU or HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\Layers and includes the full path. Therefor it will break if the file is renamed or copied.

If the file lacks a manifest you can just rename the file as setup.exe and Windows will force consent on you. That didn't come out right.

The other way around, running a program that requires admin rights without admin rights, is slightly more convoluted.

There are a couple of ways to do it, where the easiest way is to set the environment variable __COMPAT_LAYER = RunAsInvoker. Note the initial double underscore.



Using a script or by coding it is trivial to make a launcher. In AutoIt:

#NoTrayIcon
#AutoIt3Wrapper_Icon=Some icon preferably same icon as the program this is supposed to launch.ico

$RunAsInvoker_command = 'set __COMPAT_LAYER=RunAsInvoker'

Run(@ComSpec & ' /c ' & $RunAsInvoker_command & ' & start "" /D "' & @ScriptDir & '" ' & $cmdlineraw, @ScriptDir, @SW_HIDE)

Another way to more permanently change the application is to edit its manifest and simply replace level="requireAdministrator" with level="asInvoker" using your preferred manifest editor that works. They don't always work. If the file is signed, not even a hex-editor works.

That is the easy part. Then you need to make sure the program actually works running with only user rights. For this task, you'll need Procmon, the greatest tool ever made. I said that already.

UAC and Admin Approval Mode can be turned off in various ways. DO NOT DO THIS. On the contrary, if it was up to me (it isn't) I would enable Prompt for credentials for ConsentPromptBehaviorAdmin, so that even admins have to enter a password before elevating.

fredag 29 september 2017

PendingFilerenameOperations

Some installs require a reboot when finished, either by asking nicely or by forcefully restarting the computer like an asshole. Most, but not all, msi-installations respect the REBOOT=ReallySuppress flag.

Some installs however, require a reboot before the install begins.

I like to call these installs arrogant or pretentious. Also cocksure. I like that word.

These installs are so convinced about their own superiority that they refuse to install if a restart is pending for any reason.

Heck, I can upgrade my network drivers and my graphics drivers on the fly without restarting the computer, but YOUR program is so advanced and important that it cannot fathom to just copy the damn binaries unless the computer is perfectly clean.

The way to work around this is the registry entry PendingFilerenameOperations. Tucked away far down the path HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager this MULTI_SZ key containst files that supposedly could not be copied to their destination or deleted for some reason, locked files usually. They are now in TEMP limbo and needs to be renamed during the next boot.

If this key exists, at all, some installs refuse to run. It doesn't matter if the key contains nothing but crap files, installation becomes impossible. Unless you restart, of course, but that is the lame way.

Helpful pages on the web has the solution: Delete the damn key.

Well, I agree. But what if there is something important in there? Let's read it's content, remove the key, and then write everything back! This is somewhat easily done by using reg.exe in a script, if it's a simple before and after action.

I wan't to use AutoIt though, just because. First, my installer wrapper is written in AutoIt for obvious reasons. Laziness, if that wasn't obvious. Also, I wan't to clear it continously during long and complicated chains of dependent installs, many of which require restart inbetween.

It complicates things, because this key does not abide to standard MULTI_SZ rules, which does not allow empty rows, for instance.

The number of people caring about this, understanding it, having use for it and finally finding this page is propably fewer than the number of my Facebook friends (sobbing). Not even my mom reads this blog.

But here you go:


Global $PendingFilerenameOperations

Func GetPendingFilerenameOperations()

local $tmp=_RegReadPendingFilerenameOperations()

RegDelete("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager","PendingFilerenameOperations")

if $tmp<>"" Then

$tmp=StringTrimLeft($tmp,2); Remove "0x" from string, so that we can add binary strings together.

if StringRight($tmp,8)="00000000" Then
$tmp=StringTrimRight($tmp,4) ; Remove last LF if there are two.
EndIf

StringReplace($tmp,"000000","")
local $linefeeds = @extended ; Find out the number of linefeeds in the binary string. All characters ends in 00, plus 00 00 for the linefeed.

if Mod($linefeeds,2) <> 0 Then
$tmp=$tmp & "0000" ; Add back the linefeed if there was an odd number of linefeeds.
EndIf
$PendingFilerenameOperations = $PendingFilerenameOperations & $tmp
EndIf

EndFunc


Func SetPendingFilerenameOperations()

GetPendingFilerenameOperations()

if $PendingFilerenameOperations<>"" Then

;~ Count trailing Zeroes. There should always be exactly 10 when everything is said and done. 00 for the last character, 00 00 for the linefeed after the last row, and then a final 00 00 linefeed.

local $trailingZeroes=0

for $i = StringLen($PendingFilerenameOperations)-3 to 1 step -4
if StringMid($PendingFilerenameOperations,$i,4)="0000" then
$trailingZeroes = $trailingZeroes+1
Else
ExitLoop
EndIf
Next

;~ AutoIt RegWrite (or maybe BinaryToString) will add the trailing zeroes for us, contrary to documentation (or I'm lost somehow), so lets remove them:

$PendingFilerenameOperations = StringTrimRight($PendingFilerenameOperations, $trailingZeroes*4)

; Convert to string, and write it all back:

$PendingFilerenameOperations = StringReplace(BinaryToString("0x" & $PendingFilerenameOperations, 2), ChrW(0), @LF) ; According to AutoIt doc it should be @CRLF here, but that gives incorrect result.

RegWrite("HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager","PendingFilerenameOperations","REG_MULTI_SZ",$PendingFilerenameOperations)

EndIf

EndFunc


Func _RegReadPendingFilerenameOperations()

Local $hKey = 0x80000002 ; HKEY_LOCAL_MACHINE
local $lpSubKey = "SYSTEM\CurrentControlSet\Control\Session Manager"
local $lpValueName = "PendingFilerenameOperations"

;~ Open key:

$ret = DllCall("advapi32.dll", "long", "RegOpenKeyExW", "ulong_ptr", $hKey, "wstr", $lpSubKey, "dword", 0, "ulong", 0x20019, "ulong_ptr*", 0)
If @error Or ($ret[0] <> 0) Then
return ""
EndIf
local $hKey2 = $ret[5]

;~ Check type and size of PendingFilerenameOperations:

Local $ret2 = DllCall("advapi32.dll", "long", "RegQueryValueExW", "ulong_ptr", $hKey2, "wstr", $lpValueName, "ptr", 0, "dword*", 0, "ptr", 0, "dword*", 0)
If @error Or ($ret2[0] <> 0) Then
DllCall("advapi32.dll", "long", "RegCloseKey", "ulong_ptr", $hKey2)
return ""
EndIf
if $ret2[4]<>7 Then
;~ Not a REG_MULTI_SZ... Something strange is going on...

return ""
EndIf

;~ Read the damn data:

Local $keyLen = $ret2[6]

Local $keyData = DllStructCreate("byte[" & $keyLen & "]")
$ret3 = DllCall("advapi32.dll", "long", "RegQueryValueExW", "ulong_ptr", $hKey2, "wstr", $lpValueName, "ptr", 0, "dword*", 0, "ptr", DllStructGetPtr($keyData), "dword*", DllStructGetSize($keyData))
DllCall("advapi32.dll", "long", "RegCloseKey", "ulong_ptr", $hKey2)
If (Not IsArray($ret3)) Or ($ret3[0] <> 0) Then
return ""
EndIf

Return DllStructGetData($keyData, 1)

EndFunc