绕过安全限制,通过cmd执行PowerShell脚本
我的初衷是写一个自动激活系统的cmd脚本给身边的朋友使用,它需要判断当前运行的系统版本,需要记住大量的与具体的版本对应的key。
开始动手后我发现没法往下写。cmd本质上不支持“结构化编程”,这意味着它没有“函数”这个东西。它只能定义 label,用 label 来模拟函数。过去,当看到 Pascal 对自己开创的“结构化编程”很自豪时我并不是很理解,现在我理解了,原来非“结构化”的编程长这样。
写了一半后我放弃了。
听说 PowerShell 是新的Windows系统脚本语言,要不用 PowerShell 试试?
不懂语法没关系,边查边写,很快就搞定了。但是当我把脚本拷贝到别的机器运行时,duang! 脚本没法运行 - ”在此系统上禁止运行脚本“。运行 Set-ExecutionPolicy
修改了安全策略以后再次运行,duang! duang! 还是没法运行,因为没有数字签名。拜托,我只想写一个给朋友用的脚本,我的朋友们不懂得如何运行命令。难道就此放弃?如果换用其它成熟的编程语言去干这个事其实很轻松,比如 Python。但是在发布时必须附带上庞大的运行时环境。我感觉没必要。
灵光一现,可不可以通过cmd去调用powershell呢?于是,下面的方案就出现了:
所有的 PowerShell 脚本代码保存在 ps.txt
中
$pkeys = @{
'Windows Server 2022 Standard' = 'VDYBN-27WPP-V4HQT-9VMD4-VMK7H';
'Windows Server 2022 Datacenter' = 'WX4NM-KYWYW-QJJR4-XV3QB-6VM33';
'Windows Server 2019 Datacenter' = 'WMDGN-G9PQG-XVVXX-R3X43-63DFG';
'Windows Server 2019 Standard' = 'N69G4-B89J2-4G8F4-WWYCC-J464C';
'Windows Server 2019 Essentials' = 'WVDHN-86M7X-466P6-VHXV7-YY726';
'Windows Server 2016 Datacenter' = 'CB7KF-BWN84-R7R2Y-793K2-8XDDG';
'Windows Server 2016 Standard' = 'WC2BQ-8NRM3-FDDYY-2BFGV-KHKQY';
'Windows Server 2016 Essentials' = 'JCKRF-N37P4-C2D82-9YXRT-4M63B';
'Windows 11 Pro' = 'W269N-WFGWX-YVC9B-4J6C9-T83GX';
'Windows 11 Pro Education' = '6TP4R-GNPTD-KYYHQ-7B7DP-J447Y';
'Windows 11 Education' = 'NW6C2-QMPVW-D7KKK-3GKT6-VCFB2';
'Windows 11 Enterprise' = 'NPPR9-FWDCX-D2C8J-H872K-2YT43';
'Windows 10 Pro' = 'W269N-WFGWX-YVC9B-4J6C9-T83GX';
'Windows 10 Enterprise' = 'NPPR9-FWDCX-D2C8J-H872K-2YT43';
'Windows 10 Education' = 'NW6C2-QMPVW-D7KKK-3GKT6-VCFB2';
'Windows 8.1 Professional' = 'GCRJD-8NW9H-F2CDX-CCM8D-9D6T9';
'Windows 8.1 Enterprise' = 'MHF9N-XY6XB-WVXMC-BTDCT-MKKG7';
'Windows 7 Professional' = 'FJ82H-XT6CR-J8D7P-XQJJ2-GPDD4';
'Windows 7 Enterprise' = '33PXH-7Y6KF-2VJC9-XBBR8-HVTHH';
};
$product = (Get-ItemProperty -Path 'HKLM:\Software\Microsoft\Windows NT\CurrentVersion' -Name ProductName).ProductName;
echo $product;
if($pkeys.ContainsKey($product)){
$key = $pkeys[$product];
$slmgr = $Env:windir + '\System32\slmgr.vbs';
cscript $slmgr //nologo /upk;
cscript $slmgr //nologo /ipk $key;
cscript $slmgr //nologo /skms xxx.xxx.xxx;
cscript $slmgr //nologo /ato;
}
上面的代码和通常的 ps 脚本有什么区别呢?区别就是每一条语句后面都加上了可选的分号;另外就是,所有的字符串都用单引号,因为双引号要留给外层的cmd使用。下面的 bat 会把 ps 代码一行一行地读入,然后拼接成一行,最后再调用 powershell -command
去执行。完美绕过限制。
main.bat
@echo off
set src=
setlocal enableDelayedExpansion
for /f "tokens=* delims=" %%a in (%~dp0\ps.txt) do (
set "line=%%a"
set "src=!src!!line!"
)
powershell -NoLogo -command "%src%"
pause
echo on
把两个脚本放在一起,以管理员权限执行 main.bat
即可。
ps.txt 脚本中的 xxx.xxx.xxx 需要成改实际的kms服务器。自己部署 vlmcsd 其实很容易,如果不想自己部署,可用的 kms 服务器可以来这里找到: