环境
WinRAR v7.10 Beta x64
Win10 22H2 企业版 x64 + 开启 UAC 最高等级
使用 WinRAR 给一个分发的便携式应用目录打包为安装程序
尴尬的发现 WinRAR 此时最新的 v7.10 Beta 的高级自解压选项里面关于快捷方式的下面那个复选框选项 请求管理员访问权限
并不是想当然以为的那样,给创建的快捷方式设置以管理员身份运行这个选项
而是针对自解压安装包本身用的,让安装包启动时触发 UAC ,请求提升至管理员权限运行
只是它把这个复选框选项放到了快捷方式这个标签页下面
然后我这个应用必须是管理员权限启动,不然会有很多问题,然后不能简单粗暴直接关闭用户环境的 UAC
研究了半天,发现 无论是 bat/vbs/powershell 都没有提供直接的实现接口,这里当然可以换一些专业的打包工具,处理这些问题/定制随便实现,如 NSIS/Inno Setup/......,但是我不想那么麻烦,只是一个简单的打包来分发而已,没必要
有 2 个绕过的类似提供方案
- 创建一个特殊的替代快捷方式来实现
创建的快捷方式指向的是 .bat/vbs/ps1 文件,这些脚本运行时有大把办法请求提升至管理员权限,然后使用提升至管理员权限的终端来启动应用程序,比如call start ......
,就是 UAC 授权时的申请信息显示对象会是 命令提示符/powershell/vbs 脚本编译器
如下:
@echo off
:: 检查当前是否以管理员权限运行脚本
>nul 2>&1 "%SYSTEMROOT%\system32\cacls.exe" "%SYSTEMROOT%\system32\config\system"
:: 如果不是以管理员权限运行,申请提权 UAC,并重新运行脚本
if '%errorlevel%' NEQ '0' (
echo 请求管理员权限,请授权 UAC 弹窗...
echo Set UAC = CreateObject^("Shell.Application"^) > "%temp%\getadmin.vbs"
echo UAC.ShellExecute "cmd.exe", "/c ""%~s0""", "", "runas", 1 >> "%temp%\getadmin.vbs"
"%temp%\getadmin.vbs"
del "%temp%\getadmin.vbs"
exit /b
)
:: 提权成功,执行后续命令
最后还是找到了个“正招”,其实是社区的大佬逆向发现的,也是最优雅的实现方式
.lnk 快捷方式实际上是一组特殊的二进制文件,遵循 Shell Link Binary File Format 规范,完整的文件格式规范可以在微软的 [MS-SHLLINK] 文档中找到
.lnk 文件中有一个叫做 SHELL_LINK_HEADER 的结构,其中包含了一个 LinkFlags 字段,我们修改快捷方式文件的二进制数据来设置管理员权限标志,在这个 LinkFlags 中,第 21 个字节的第 6 位(0x20)表示 "Run as Administrator" 标志,当这个位被设置为 1 时,Windows 会以管理员权限运行目标程序经过我的测试可行,修改快捷方式文件 LNK 的第 21 字节处的偏移量,使用按位或运算该处原本的十六进制值,具体是将原本的该处十六进制值转为二进制,与 十六进制的
0x20
的二进制值进行按位 OR 运算
就是将从右往左数的第6位二进制位设为1
,其他位保持不变,因为这里原本的数据是 0x00,二进制转换后时0010 0000 (0x20)
,OR
运算后结果肯定还是0010 0000 (0x20)
,写入该处的数据保存,此时,该快捷方式的属性那里,以管理员身份运行的复选框是不会勾选的,√ 不勾选已经没影响了,2 者是独立的,手动勾选保存,再不勾选保存也不会影响到快捷方式
但是运行时已经完全不一样了,会触发 UAC,并提权至管理员权限运行,这种标志位的管理员权限优先级比快捷方式右键属性的设置更高
以下是实现脚本, 使用了 VBS + BAT,该脚本可以在标准用户的权限下运行,无需管理员权限,也不会触发 UAC
@echo off
:: 设置快捷方式路径
set shortcutPath=%userprofile%\Desktop\Example.lnk
:: 使用当前目录(安装目录)设置路径,自行替换 `%cd%` 后面部分为你的实际路径
set "targetPath=%cd%\Example\114514\Example.exe"
set "workingDirectory=%cd%\Example\114514"
:: 调用 VBS 创建快捷方式
echo Set WshShell = CreateObject("WScript.Shell") > createShortcut.vbs
echo Set Shortcut = WshShell.CreateShortcut("%shortcutPath%") >> createShortcut.vbs
echo Shortcut.TargetPath = "%targetPath%" >> createShortcut.vbs
echo Shortcut.WorkingDirectory = "%workingDirectory%" >> createShortcut.vbs
echo Shortcut.IconLocation = "%targetPath%, 0" >> createShortcut.vbs
echo Shortcut.WindowStyle= 3 >> createShortcut.vbs
echo Shortcut.Save >> createShortcut.vbs
cscript //nologo createShortcut.vbs
del createShortcut.vbs
:: 调用 PowerShell 设置管理员权限运行标志位,设置 `0x15`(十六进制) 字节偏移量位置的数据的第 6 位设置为 `1`(0x20),其余位保持不变
powershell -Command "& { $bytes = [System.IO.File]::ReadAllBytes('%shortcutPath%'); $bytes[0x15] = $bytes[0x15] -bor 0x20; [System.IO.File]::WriteAllBytes('%shortcutPath%', $bytes) }"
在 Windows 10 22H2 企业版 x64 测试通过,环境是开启 UAC 最高级别的
- 其他说明:根据自己的实际情况修改替换路径
最后只需要把这个 bat 丢到待打包的安装目录里面,在 WinRAR 自解压选项的解压后运行加载该脚本即可,还可以使用 WinRAR 内置提供的参数选项 <Hide>
来隐藏执行的程序/脚本,示例
<Hide>robocopy "Example\114514" "%appdata%\1145141919810" /E /COPYALL /NFL /NDL /R:1 /W:1 /V /Z /PURGE
<Hide>Example\asAdmin.bat
<Hide>cmd /c start "" /MAX /REALTIME "%cd%\Example\114514\Example.exe"
鸣谢
https://stackoverflow.com/questions/11162802/how-can-i-use-jscript-to-create-a-shortcut-that-uses-run-as-administrator
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/c3376b21-0931-45e4-b2fc-a48ac0e60d15
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-shllink/ae350202-3ba9-4790-9e9e-98935f4ee5af