Решал я тут сегодня вполне простую и житейскую проблему. Стоит на компе Windows, хочется ее переустановить, только ключ никто не помнит (а Windows-то кошерный, лицензионный). Задача: вытрясти из уже установленной Windows ключ, с которым ее ставили. В принципе в Интернетах полно программулек, решающих эту проблему, но это неспортивно и не модно. А модно сейчас писать скрипты на PowerShell.
Известная тема, что Windows (да и вообще большинство продуктов Microsoft) хранит инфу о серийниках в реестре в шифрованном виде. Шифрованном – это сильно сказано, никаких особых заморочек там нет (можно взорвать свой моск здесь), нужно просто знать, где искать и как расшифровывать.
В общем, начинаем с простого скрипта:
$rpk = (Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\" -Name DigitalProductId).DigitalProductId
$i = 28
$rpkOffset = 52
$PossibleChars = "BCDFGHJKMPQRTVWXY2346789"
do {
$Accumulator = 0
$j = 14
do {
$Accumulator = $Accumulator * 256
$Accumulator = $rpk[$j + $rpkOffset] + $Accumulator
$Accumulator / 24 -match "^\d*" | Out-Null
$rpk[$j + $rpkOffset] = $matches[0] -band 255
$Accumulator = $Accumulator % 24
$j--
} while ($j -ge 0)
$i--
$ProductKey = $PossibleChars.Substring($Accumulator, 1) + $ProductKey
if ((29 - $i) % 6 -eq 0 -and $i -ne -1) {
$i--
$ProductKey = "-" + $ProductKey
}
} while ($i -ge 0)
$ProductKey
Скрипт тупо расшифровывает серийник из реестра и выводит его в консоль. Однако скрипт еще недостаточно модный – дополним его возможностью получать инфу с нескольких компов, а также оптимизируем алгоритм раскодирования base-24. В итоге имеем скрипт №2:
param ($targets = ".")
$hklm = 2147483650
$regPath = "Software\Microsoft\Windows NT\CurrentVersion"
$regValue = "DigitalProductId"
Foreach ($target in $targets) {
$productKey = $null
$win32os = $null
$wmi = [WMIClass]"\\$target\root\default:stdRegProv"
$data = $wmi.GetBinaryValue($hklm,$regPath,$regValue)
$binArray = ($data.uValue)[52..66]
$charsArray = "B","C","D","F","G","H","J","K","M","P","Q","R","T","V","W","X","Y","2","3","4","6","7","8","9"
For ($i = 24; $i -ge 0; $i--) {
$k = 0
For ($j = 14; $j -ge 0; $j--) {
$k = $k * 256 -bxor $binArray[$j]
$binArray[$j] = [math]::truncate($k / 24)
$k = $k % 24
}
$productKey = $charsArray[$k] + $productKey
If (($i % 5 -eq 0) -and ($i -ne 0)) {
$productKey = "-" + $productKey
}
}
$win32os = Get-WmiObject Win32_OperatingSystem -computer $target
$obj = New-Object Object
$obj | Add-Member Noteproperty Computer -value $target
$obj | Add-Member Noteproperty Caption -value $win32os.Caption
$obj | Add-Member Noteproperty CSDVersion -value $win32os.CSDVersion
$obj | Add-Member Noteproperty OSArch -value $win32os.OSArchitecture
$obj | Add-Member Noteproperty BuildNumber -value $win32os.BuildNumber
$obj | Add-Member Noteproperty RegisteredTo -value $win32os.RegisteredUser
$obj | Add-Member Noteproperty ProductID -value $win32os.SerialNumber
$obj | Add-Member Noteproperty ProductKey -value $productkey
$obj
}
Скрипт также более правильный тем, что возвращает объект PowerShell, что позволяет фильтровать и сортировать его результаты, а также использовать их при вызове других скриптов.
Сразу небольшая поправочка – данный метод не работает с ключами VA 2.0, т.е. с MAK и KMS-ключами для Windows 2008 и Windows 7 (возможно и для Windows Vista – не имею возможности проверить), т.к. при использовании VA 2.0 в нужном месте реестра лежат нули, которые раскодируются в ключ BBBBB-BBBBB-BBBBB-BBBBB-BBBBB, а инфа хранится в файле C:\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\SoftwareLicensing\tokens.dat, который пока не ясно, как расшифровывать…