Skip to main content

Decode PKey980 / PKey986 Keys

Overview

  • How to use it? Instructions can be found here.
  • The Decode PKey980 / PKey986 option decodes the internal structure of Pre-Vista product keys (Windows 98 to XP, Office 2000 to 2007, etc.), extracting the Upgrade flag, Channel ID, Sequence, Hash, Signature, and Auth fields.
  • Key Checker uses PidGenX.dll to validate a key, but also runs this manual decode in the background because PidGenX.dll does not show all the internal fields like Hash, Signature, and Auth.
  • This option works for both PKey980 (BINK1998) (Windows 98/2000/XP) and PKey986 (BINK2002) (Windows Server 2003) algorithms.
  • PKey980/986 keys do not contain an N in the key and use the standard Base24 alphabet.

How does it work?

A PKey980 / PKey986 key is a Base24-encoded number. Decoding it means converting that encoded form back into a plain integer, then reading each field out of it by position. The exact fields depend on the product's BINK ID (BINK1998 vs BINK2002).

The steps below walk through this using the well-known public key FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8 (a BINK1998 Windows XP key) as a real example.

Step 1: The alphabet and character mapping

The Base24 alphabet has 24 characters (BCDFGHJKMPQRTVWXY2346789), each with a digit value from 0 to 23:

0 1 2 3 4 5 6 7 8 9 10 11
B C D F G H J K M P Q R

12 13 14 15 16 17 18 19 20 21 22 23
T V W X Y 2 3 4 6 7 8 9

The characters A, E, I, O, U, 1, 5, S, L, Z, 0, and N are intentionally excluded to avoid visual confusion.

Remove the dashes from FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8. The clean key is FCKGWRHQQ2YXRKT8TG6W2B7Q8.

The 25 characters are each looked up in the alphabet:

F = 3, C = 1, K = 7, G = 4, W = 14,
R = 11, H = 5, Q = 10, Q = 10, 2 = 17,
Y = 16, X = 15, R = 11, K = 7, T = 12,
8 = 22, T = 12, G = 4, 6 = 20, W = 14,
2 = 17, B = 0, 7 = 21, Q = 10, 8 = 22

In PowerShell, this mapping is done using:

$alpha = 'BCDFGHJKMPQRTVWXY2346789'
$key = 'FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8'
$clean = $key.Replace('-', '')

$digits = $clean.ToCharArray() | ForEach-Object { [bigint]$alpha.IndexOf($_) }

Step 2: Base24 decoding

Those 25 digits are decoded into a single integer the same way you would convert a number from any base: start with 0, then for each digit multiply what you have by the base (24) and add the next digit. The first few steps look like this:

start: 0
digit 3 (F): 0 × 24 + 3 = 3
digit 1 (C): 3 × 24 + 1 = 73
digit 7 (K): 73 × 24 + 7 = 1759
digit 4 (G): 1759 × 24 + 4 = 42220
... and so on for all 25 digits

After all 25 digits, the result is a very large integer that contains all the key's fields packed together. It easily exceeds 64 bits.

$num = $digits | ForEach-Object -Begin { $acc = [bigint]0 } `
-Process { $acc = $acc * 24 + $_ } `
-End { $acc }

Result:

$num = 4073432328284217470737194135797830

Step 3: Extract fields by shifting and masking

The way fields are packed into that number depends on the key's BINK ID.

  • If the BINK ID is less than 0x40, the key uses the BINK1998 format.
  • If the BINK ID is 0x40 or greater, the key uses the BINK2002 format.

To extract a field, you shift the number right to bring it down to bit 0, then AND it with a mask to keep only its bits.

A mask is a number made of exactly as many 1-bits as the field is wide. For example, the Serial field is 30 bits wide, so its mask is thirty 1-bits (0x3FFFFFFF).

To see this with the actual value of $num, first convert it to binary (padded to 115 bits):

0001100100011010101111100011011100010000000111000011100000011101100010001011111100111001100010010110100000001000110

The Serial field sits at bits 1–30, meaning we drop the first bit (the Upgrade bit) and take the next 30 bits:

$num (binary) : 0001100100011010101111100011011100010000000111000011100000011101100010001011111100111001100010010110100000001000110
shift right by 1 : 0000110010001101010111110001101110001000000011100001110000001110110001000101111110011100110001001011010000000100011
mask (0x3FFFFFFF): 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000111111111111111111111111111111
result : 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000100110001001011010000000100011

Extracted Serial Value:

  • Binary: 100110001001011010000000100011
  • Decimal: 640000035

BINK1998 Format (BINK ID < 0x40)

FieldStart bitLengthMaskDescription
Upgrade010x11 if this is an upgrade key, 0 otherwise.
Serial1300x3FFFFFFF30-bit value containing the Channel and Sequence.
Hash31280xFFFFFFFA 28-bit hash used to verify the key.
Signature59560xFFFFFFFFFFFFFFA 56-bit cryptographic signature.

The Serial field is further split into two parts:

  • Channel: Serial / 1000000
  • Sequence: Serial % 1000000
$upg = [int] ($num -band 0x1)
$serial = [int] (($num -shr 1) -band 0x3FFFFFFF)
$hash = [int] (($num -shr 31) -band 0xFFFFFFF)
$sig = [int64](($num -shr 59) -band 0xFFFFFFFFFFFFFF)

$channel = [Math]::Floor($serial / 1000000)
$seq = $serial % 1000000

Result for FCKGW-RHQQ2-YXRKT-8TG6W-2B7Q8 (BINK ID: 0x2E):

Upgrade : 0
Serial : 640000035
Channel : 640
Sequence : 35
Hash : 30968819
Signature : 7066278687677496

BINK2002 Format (BINK ID >= 0x40)

For newer Pre-Vista keys like Windows Server 2003, the layout changes slightly to accommodate an Authentication bitfield. The combined 30-bit Serial field is removed, leaving only a direct 10-bit Channel ID and no Sequence number:

FieldStart bitLengthMaskDescription
Upgrade010x11 if this is an upgrade key, 0 otherwise.
Channel1100x3FFDirect 10-bit Channel ID.
Hash11310x7FFFFFFFA 31-bit hash.
Signature42620x3FFFFFFFFFFFFFFFA 62-bit cryptographic signature.
Auth104100x3FFA 10-bit Authentication field.
$upg = [int] ($num -band 0x1)
$channel = [int] (($num -shr 1) -band 0x3FF)
$hash = [int] (($num -shr 11) -band 0x7FFFFFFF)
$sig = [int64](($num -shr 42) -band 0x3FFFFFFFFFFFFFFF)
$auth = [int] (($num -shr 104) -band 0x3FF)

Result for VV7KW-R29XM-YWDVF-BT8Q2-R767M (BINK ID: 0x5A):

Upgrade : 0
Channel : 640
Hash : 487100089
Signature : 4196257248039765342
Auth : 892

Note on Key Validation

The steps outlined above only decode the key to its internal field structure; they do not mathematically validate the key.

With Elliptic Curve Cryptography (ECC) math and BINK ID data, it is possible to validate the cryptographic signature and extract the full Serial for BINK2002 keys.

However, since Key Checker relies on PidGenX.dll to natively validate the keys and return the primary details, those complex ECC validation steps are not included in the manual decode scripts. This manual decode process is still run in the background purely because PidGenX.dll does not show properties like the Hash, Signature, and Auth fields.


Feedback / Troubleshooting