Created
June 2, 2023 07:25
-
-
Save WitherOrNot/80f978c018f4157ecd079e9cb26269b3 to your computer and use it in GitHub Desktop.
All the code you need to activate Windows XP w/o WPA bypasses
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Product Key Generator - Windows XP" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Paste JSON object for BINK data here:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Windows XP Professional Retail (Bink ID 2C)\n", | |
"key_data = {\n", | |
" \"p\": 24412280675538104642884792561502783185577987209710041026341163083973933860854736635268965257725055809364646140091249,\n", | |
" \"a\": 1,\n", | |
" \"b\": 0,\n", | |
" \"B\": [\n", | |
" 21673361717619259910600499419800485528178801849923454062050055236231939594233283543796077751210469045350919066368895,\n", | |
" 5232476492611604888729825305639232005017822876108144652169892952989580351454246958886421453535493897842819359154864\n", | |
" ],\n", | |
" \"K\": [\n", | |
" 21551722775458524408480112576069559265917312687549112053580919391285918530584174752292844347621326558272739603979057,\n", | |
" 13463977158522661542654520438933687107907187215503371589980428235633526671841388652148099285621876350916055100879930\n", | |
" ],\n", | |
" \"order\": 55681564377333977,\n", | |
" \"private_key\": 30951839223306173\n", | |
"}" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Run this cell to generate key" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"scrolled": false | |
}, | |
"outputs": [], | |
"source": [ | |
"import hashlib\n", | |
"\n", | |
"# p = order of field Fp\n", | |
"# Fp = Galois field of order p\n", | |
"# E = Elliptic curve y^2 = x^3 + ax + b over Fp\n", | |
"# B = generator on E\n", | |
"# K = inverse of public key\n", | |
"# order = order of E\n", | |
"\n", | |
"p = key_data[\"p\"]\n", | |
"Fp = GF(p)\n", | |
"E = EllipticCurve(Fp, [0, 0, 0, key_data[\"a\"], key_data[\"b\"]])\n", | |
"B = E.point(key_data[\"B\"])\n", | |
"K = E.point(key_data[\"K\"])\n", | |
"order = key_data[\"order\"]\n", | |
"private_key = -key_data[\"private_key\"] % order\n", | |
"\n", | |
"# PID of product key\n", | |
"pid = 756_696969\n", | |
"\n", | |
"# Key alphabet\n", | |
"KCHARS = \"BCDFGHJKMPQRTVWXY2346789\"\n", | |
"\n", | |
"def int_to_bytes(n, l=None):\n", | |
" n = int(n)\n", | |
" \n", | |
" if not l:\n", | |
" l = (n.bit_length() + 7) // 8\n", | |
" \n", | |
" return n.to_bytes(l, byteorder=\"little\")\n", | |
"\n", | |
"def encode_pkey(n):\n", | |
" out = \"\"\n", | |
" \n", | |
" while n > 0:\n", | |
" out = KCHARS[n % 24] + out\n", | |
" n //= 24\n", | |
" \n", | |
" out = \"-\".join([out[i:i+5] for i in range(0, len(out), 5)])\n", | |
" return out\n", | |
"\n", | |
"pid <<= 1\n", | |
"\n", | |
"while True:\n", | |
" k = getrandbits(384)\n", | |
" r = k * B\n", | |
" x, y = r.xy()\n", | |
"\n", | |
" md = hashlib.sha1(int_to_bytes(pid, 4) + int_to_bytes(x, 48) + int_to_bytes(y, 48)).digest()\n", | |
" h = int.from_bytes(md[:4], byteorder=\"little\") >> 4\n", | |
" h &= 0xfffffff\n", | |
"\n", | |
" s = int(abs((private_key * h + k) % order))\n", | |
" raw_pkey = s << 59 | h << 31 | pid\n", | |
" \n", | |
" print(hex(pid)[2:], hex(h)[2:], hex(s)[2:], hex(raw_pkey)[2:])\n", | |
" \n", | |
" if raw_pkey >> 96 < 0x40000:\n", | |
" break\n", | |
"\n", | |
"print(encode_pkey(raw_pkey))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Key decoder (run above cell first)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"scrolled": true | |
}, | |
"outputs": [], | |
"source": [ | |
"def decode_pkey(k):\n", | |
" k = k.replace(\"-\", \"\")\n", | |
" out = 0\n", | |
" \n", | |
" for c in k:\n", | |
" out *= 24\n", | |
" out += KCHARS.index(c)\n", | |
" \n", | |
" return out\n", | |
"\n", | |
"pkey = input(\"Product Key (dashes optional): \")\n", | |
"raw_pkey = decode_pkey(pkey)\n", | |
"\n", | |
"kpid = (raw_pkey & 0x7fffffff) >> 1\n", | |
"verify = (kpid // 1000000) == ((pid >> 1) // 1000000)\n", | |
"print(kpid, pid >> 1)\n", | |
"\n", | |
"if verify:\n", | |
" h = (raw_pkey >> 31) & 0xfffffff\n", | |
" s = (raw_pkey >> 59) & 0x7ffffffffffffff\n", | |
"\n", | |
" r = h * K + s * B\n", | |
" x, y = r.xy()\n", | |
"\n", | |
" md = hashlib.sha1(int_to_bytes(kpid << 1, 4) + int_to_bytes(x, 48) + int_to_bytes(y, 48)).digest()\n", | |
" hp = int.from_bytes(md[:4], byteorder=\"little\") >> 4\n", | |
" hp &= 0xfffffff\n", | |
"\n", | |
" print(h, hp)\n", | |
" \n", | |
" if h == hp:\n", | |
" print(\"Valid key\")\n", | |
" else:\n", | |
" print(\"Invalid key\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Confirmation ID generator" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import hashlib\n", | |
"\n", | |
"# order of field Fp \n", | |
"p = 0x16A6B036D7F2A79\n", | |
"# Galois field of order p\n", | |
"Fp = GF(p)\n", | |
"# Polynomial field Fp[x] over Fp\n", | |
"Fpx.<x> = Fp[]\n", | |
"# Hyperellptic curve function\n", | |
"F = x^5+0x1400606322B3B04*x^4+0x1400606322B3B04*x^3+0x44197B83892AD0*x^2+0x21840136C85381*x\n", | |
"# Hyperelliptic curve E: y^2 = F(x) over Fp\n", | |
"E = HyperellipticCurve(F)\n", | |
"# The jacobian over E\n", | |
"J = E.jacobian()\n", | |
"\n", | |
"# This constant inverts multiplication by 0x1001 in verification\n", | |
"# My best guess for how it was calculated: INV = 0x10001^-1 (mod |J|)\n", | |
"# |J| is hard to compute, how can we calculate for other curves?\n", | |
"INV = 0x40DA7C36D44C04E21B9D10F127C1\n", | |
"\n", | |
"# Key to decrypt installation IDs\n", | |
"IID_KEY = b'\\x6A\\xC8\\x5E\\xD4'\n", | |
"\n", | |
"# Validate installation ID checksum\n", | |
"def validate_cksum(n):\n", | |
" print(\"Checksumming installation ID...\")\n", | |
" n = n.replace(\"-\", \"\")\n", | |
"\n", | |
" cksum = 0\n", | |
" for i, k in enumerate(map(int, n)):\n", | |
" if (i + 1) % 6 == 0 or i == len(n) - 1:\n", | |
" print(\"Expected last digit\", cksum % 7, \"got\", k)\n", | |
" if cksum % 7 != k:\n", | |
" return None\n", | |
" \n", | |
" cksum = 0\n", | |
" else:\n", | |
" cksum += k * (i % 2 + 1)\n", | |
" \n", | |
" parts = [n[i:i+5] for i in range(0, len(n), 6)]\n", | |
" n_out = \"\".join(parts)\n", | |
" \n", | |
" if len(n_out) == 42:\n", | |
" n_out = n_out[:-1]\n", | |
" \n", | |
" if len(n_out) != 45 and len(n_out) != 41:\n", | |
" return None\n", | |
" \n", | |
" return int(\"\".join(parts))\n", | |
"\n", | |
"# Insert checksum digits into confirmation ID\n", | |
"def add_cksum(n):\n", | |
" cksums = []\n", | |
" n = str(n).zfill(35)\n", | |
" parts = [n[i:i+5] for i in range(0, len(n), 5)]\n", | |
" \n", | |
" for p in parts:\n", | |
" cksum = 0\n", | |
" \n", | |
" for i, k in enumerate(map(int, p)):\n", | |
" cksum += k * (i % 2 + 1)\n", | |
" \n", | |
" cksums.append(str(cksum % 7))\n", | |
" \n", | |
" n_out = \"\"\n", | |
" \n", | |
" for i in range(7):\n", | |
" n_out += parts[i] + cksums[i] + (\"-\" if i != 6 else \"\")\n", | |
" \n", | |
" return n_out\n", | |
"\n", | |
"def encrypt(decrypted, key):\n", | |
" size_half = len(decrypted) // 2\n", | |
" size_half_dwords = size_half - (size_half % 4)\n", | |
" last = decrypted[size_half*2:]\n", | |
" decrypted = decrypted[:size_half*2]\n", | |
" for i in range(4):\n", | |
" first = decrypted[:size_half]\n", | |
" second = decrypted[size_half:]\n", | |
" sha1_result = hashlib.sha1(second + key).digest()\n", | |
" sha1_result = (sha1_result[:size_half_dwords] +\n", | |
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n", | |
" decrypted = second + bytes(x^^y for x,y in zip(first, sha1_result))\n", | |
" return decrypted + last\n", | |
"\n", | |
"def decrypt(encrypted, key):\n", | |
" size_half = len(encrypted) // 2\n", | |
" size_half_dwords = size_half - (size_half % 4)\n", | |
" last = encrypted[size_half*2:]\n", | |
" encrypted = encrypted[:size_half*2]\n", | |
" for i in range(4):\n", | |
" first = encrypted[:size_half]\n", | |
" second = encrypted[size_half:]\n", | |
" sha1_result = hashlib.sha1(first + key).digest()\n", | |
" sha1_result = (sha1_result[:size_half_dwords] +\n", | |
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n", | |
" encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first\n", | |
" return encrypted + last\n", | |
"\n", | |
"# Find v of divisor (u, v) of curve y^2 = F(x)\n", | |
"def find_v(u):\n", | |
" f = F % u\n", | |
" c2 = u[1]^2 - 4 * u[0]\n", | |
" c1 = 2 * f[0] - f[1] * u[1]\n", | |
" \n", | |
" if c2 == 0:\n", | |
" if c1 == 0:\n", | |
" return None\n", | |
" \n", | |
" try:\n", | |
" v1 = sqrt(f[1]^2 / (2 * c1))\n", | |
" v1.lift()\n", | |
" except:\n", | |
" return None\n", | |
" else:\n", | |
" try:\n", | |
" d = 2 * sqrt(f[0]^2 + f[1] * (f[1] * u[0] - f[0] * u[1]))\n", | |
" v1_1 = sqrt((c1 - d)/c2)\n", | |
" v1_2 = sqrt((c1 + d)/c2)\n", | |
" except:\n", | |
" return None\n", | |
"\n", | |
" try:\n", | |
" v1_1.lift()\n", | |
" v1 = v1_1\n", | |
" except:\n", | |
" try:\n", | |
" v1_2.lift()\n", | |
" v1 = v1_2\n", | |
" except:\n", | |
" return None\n", | |
" \n", | |
" v0 = (f[1] + u[1] * v1^2) / (2 * v1)\n", | |
" v = v0 + v1 * x\n", | |
" \n", | |
" assert (v^2 - f) % u == 0\n", | |
" return v\n", | |
"\n", | |
"# unpack&decrypt installationId\n", | |
"installationId = validate_cksum(input(\"Installation ID (dashes optional): \"))\n", | |
"print(installationId)\n", | |
"\n", | |
"if not installationId:\n", | |
" raise Exception(\"Invalid Installation ID (checksum fail)\")\n", | |
"\n", | |
"installationIdSize = 19 if len(str(installationId)) > 41 else 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))\n", | |
"iid = int(installationId).to_bytes(installationIdSize, byteorder='little')\n", | |
"iid = decrypt(iid, IID_KEY)\n", | |
"hwid = iid[:8]\n", | |
"productid = int.from_bytes(iid[8:17], byteorder='little')\n", | |
"productkeyhash = iid[17:]\n", | |
"pid1 = productid & ((1 << 17) - 1)\n", | |
"pid2 = (productid >> 17) & ((1 << 10) - 1)\n", | |
"pid3 = (productid >> 27) & ((1 << 25) - 1)\n", | |
"version = (productid >> 52) & 7\n", | |
"pid4 = productid >> 55\n", | |
"\n", | |
"assert version == (4 if len(iid) == 17 else 5)\n", | |
"\n", | |
"key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')\n", | |
"\n", | |
"data = [0x00] * 14\n", | |
"\n", | |
"print(\"\\nConfirmation IDs:\")\n", | |
"\n", | |
"for i in range(0x81):\n", | |
" data[7] = i\n", | |
" # Encrypt conf ID, find u of divisor (u, v)\n", | |
" encrypted = encrypt(bytes(data), key)\n", | |
" encrypted = int.from_bytes(encrypted, byteorder=\"little\")\n", | |
" x1, x2 = Fp(encrypted % p), Fp((encrypted // p) + 1)\n", | |
" u1, u0 = x1 * 2, (x1 ^ 2) - ((x2 ^ 2) * 43)\n", | |
" u = x^2 + u1 * x + u0\n", | |
"\n", | |
" # Generate original divisor\n", | |
" v = find_v(u)\n", | |
" \n", | |
" if not v:\n", | |
" continue\n", | |
" \n", | |
" d2 = J(u, v)\n", | |
" divisor = d2 * INV\n", | |
" \n", | |
" # Get x1 and x2\n", | |
" roots = [x for x, y in divisor[0].roots()]\n", | |
"\n", | |
" if len(roots) > 0:\n", | |
" y = [divisor[1](r) for r in roots]\n", | |
" x1 = (-roots[0]).lift()\n", | |
" x2 = (-roots[1]).lift()\n", | |
"\n", | |
" if (x1 > x2) or (y[0].lift() % 2 != y[1].lift() % 2):\n", | |
" x1 = (-roots[1]).lift()\n", | |
" x2 = (-roots[0]).lift()\n", | |
" else:\n", | |
" x2 = (divisor[0][1] / 2).lift()\n", | |
" x1 = sqrt((x2^2 - divisor[0][0]) / 43).lift() + p\n", | |
"\n", | |
" # Win\n", | |
" conf_id = x1 * (p + 1) + x2\n", | |
" conf_id = add_cksum(conf_id)\n", | |
" print(conf_id)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Confirmation ID decoder/validator (made by diamondggg)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import hashlib\n", | |
"\n", | |
"# 226512-274743-842923-777124-961370-722240-570042-517722-757426\n", | |
"installationId = 114535500880440159787527912804896629001083118\n", | |
"installationIdSize = 19 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))\n", | |
"# all three of following are valid generated\n", | |
"# 013705-060122-603141-961392-086136-909901-494476\n", | |
"confirmationId = 15771960290497900806797040541467113\n", | |
"# 022032-220754-159721-909624-985141-504586-914001\n", | |
"#confirmationId = 02203220751597290962985145045891400\n", | |
"# 137616-847280-708585-827476-874935-313366-790880\n", | |
"#confirmationId = 13761847287085882747874933133679088\n", | |
"\n", | |
"def decrypt(encrypted, key):\n", | |
" size_half = len(encrypted) // 2\n", | |
" size_half_dwords = size_half - (size_half % 4)\n", | |
" last = encrypted[size_half*2:]\n", | |
" encrypted = encrypted[:size_half*2]\n", | |
" for i in range(4):\n", | |
" first = encrypted[:size_half]\n", | |
" second = encrypted[size_half:]\n", | |
" sha1_result = hashlib.sha1(first + key).digest()\n", | |
" sha1_result = (sha1_result[:size_half_dwords] +\n", | |
" sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])\n", | |
" encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first\n", | |
" return encrypted + last\n", | |
"\n", | |
"# unpack&decrypt installationId\n", | |
"iid = int(installationId).to_bytes(installationIdSize, byteorder='little')\n", | |
"iid = decrypt(iid, b'\\x6A\\xC8\\x5E\\xD4')\n", | |
"hwid = iid[:8]\n", | |
"productid = int.from_bytes(iid[8:17], byteorder='little')\n", | |
"productkeyhash = iid[17:]\n", | |
"pid1 = productid & ((1 << 17) - 1)\n", | |
"pid2 = (productid >> 17) & ((1 << 10) - 1)\n", | |
"pid3 = (productid >> 27) & ((1 << 25) - 1)\n", | |
"version = (productid >> 52) & 7\n", | |
"pid4 = productid >> 55\n", | |
"\n", | |
"assert version == (4 if len(iid) == 17 else 5)\n", | |
"\n", | |
"key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')\n", | |
"# productkeyhash is not used for validation, it exists just to allow the activation server to reject keygenned pids\n", | |
"\n", | |
"# now the math\n", | |
"\n", | |
"p = 0x16A6B036D7F2A79\n", | |
"Fp = GF(p)\n", | |
"Fpx.<x> = Fp[]\n", | |
"E = HyperellipticCurve(x^5+0x1400606322B3B04*x^4+0x1400606322B3B04*x^3+0x44197B83892AD0*x^2+0x21840136C85381*x)\n", | |
"J = E.jacobian()\n", | |
"\n", | |
"# deserialize divisor\n", | |
"x1 = confirmationId // (p + 1)\n", | |
"x2 = confirmationId % (p + 1)\n", | |
"if x1 <= p:\n", | |
" # two or less points over GF(p)\n", | |
" point1 = E.lift_x(Fp(-x1)) if x1 != p else None\n", | |
" point2 = E.lift_x(Fp(-x2)) if x2 != p else None\n", | |
" if point1 is not None and point2 is not None:\n", | |
" # there are 4 variants of how lift_x() could select both y-s\n", | |
" # we don't distinguish D and -D, but this still leaves 2 variants\n", | |
" # the chosen one is encoded by order of x1 <=> x2\n", | |
" lastbit1 = point1[1].lift() & 1\n", | |
" lastbit2 = point2[1].lift() & 1\n", | |
" if x2 < x1:\n", | |
" if lastbit1 == lastbit2:\n", | |
" point2 = E(point2[0], -point2[1])\n", | |
" else:\n", | |
" if lastbit1 != lastbit2:\n", | |
" point2 = E(point2[0], -point2[1])\n", | |
" point1 = J(point1) if point1 is not None else J(0)\n", | |
" point2 = J(point2) if point2 is not None else J(0)\n", | |
" divisor = point1 + point2\n", | |
"else:\n", | |
" # a pair of conjugate points over GF(p^2)\n", | |
" f = (x+x2)*(x+x2)-43*x1*x1 # 43 is the minimal quadratic non-residue in Fp\n", | |
" Fp2 = GF(p^2)\n", | |
" point1 = E.lift_x(f.roots(Fp2)[0][0])\n", | |
" point2 = E(Fp2)(point1[0].conjugate(), point1[1].conjugate())\n", | |
" divisor = J(Fp2)(point1) + J(Fp2)(point2)\n", | |
" divisor = J(Fpx(divisor[0]), Fpx(divisor[1])) #return from Fp2 to Fp\n", | |
"\n", | |
"d2 = divisor * 0x10001\n", | |
"assert d2[0].degree() == 2\n", | |
"x1 = d2[0][1]/2\n", | |
"x2 = sqrt((x1*x1-d2[0][0])/43)\n", | |
"\n", | |
"encrypted = x1.lift() + (x2.lift() - 1) * p\n", | |
"encrypted = int(encrypted).to_bytes(14,byteorder='little')\n", | |
"\n", | |
"# end of the math\n", | |
"decrypted = decrypt(encrypted, key)\n", | |
"print(decrypted.hex())\n", | |
"# 0000000000000001000000000000 for the first confirmationId\n", | |
"# 0000000000000002000000000000 for the second confirmationId\n", | |
"# 0000000000000006000000000000 for the last confirmationId\n", | |
"assert decrypted[8:] == b'\\0' * 6\n", | |
"assert decrypted[7] <= 0x80\n", | |
"# all zeroes in decrypted[0:7] are okay for the checker\n", | |
"# more precisely: if decrypted[6] == 0, first 6 bytes can be anything\n", | |
"# otherwise, decrypted[0] = length, and decrypted[1:1+length] must match first length bytes of sha1(product key)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "SageMath 9.0", | |
"language": "sage", | |
"name": "sagemath" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.8.10" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 5 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment