mirror of
https://github.com/Neo-Desktop/WindowsXPKg
synced 2026-10-31 13:55:20 +02:00
* Add Wither's gists as references * remove broken ones --------- Co-authored-by: WitherOrNot <damemem@gmail.com>
16 KiB
16 KiB
In [ ]:
"""
# MS Plus! DME
# order of field Fp
p = 0x16A5DABA0605983
# Coefficients of F
coeffs = [0x334f24f75caa0e, 0x1392ff62889bd7b, 0x135131863ba2db8, 0x153208e78006010, 0x163694f26056db, 1]
# This constant inverts multiplication by 0x10001 in verification
INV = 0x01c61212ece6107c4254c43a5d1181
# Key to decrypt installation IDs
IID_KEY = b'\x6A\xC8\x5E\xD4'
#"""
#"""
# Office XP/2003
# order of field Fp
p = 0x16E48DD18451FE9
# Coefficients of F
coeffs = [0, 0xE5F5ECD95C8FD2, 0xFF28276F11F61, 0xFB2BD9132627E6, 0xE5F5ECD95C8FD2, 1]
# This constant inverts multiplication by 0x10001 in verification
INV = 0x01fb8cf48a70dfefe0302a1f7a5341
# Key to decrypt installation IDs
IID_KEY = b'\x5A\x30\xB9\xF3'
#"""
"""
# Whistler 2428 (could be others)
# order of field Fp
p = 0x16BD82821354FA3
# Coefficients of F
coeffs = [0, 0xDEFD8C5651954F, 0xA23AA12556ECE5, 0x89D79AD61B786D, 0xCCA087F0A6A4A4, 1]
# This constant inverts multiplication by 0x10001 in verification
INV = 0xd9ed873ed84a45761c23fd7fafd1
# Key to decrypt installation IDs
IID_KEY = b'\x6A\xC8\x5E\xD4'
#"""
"""
# Windows XP/Server 2003/Longhorn Pre-Reset
# order of field Fp
p = 0x16A6B036D7F2A79
# Coefficients of F
coeffs = [0, 0x21840136C85381, 0x44197B83892AD0, 0x1400606322B3B04, 0x1400606322B3B04, 1]
# This constant inverts multiplication by 0x10001 in verification
INV = 0x40DA7C36D44C04E21B9D10F127C1
# Key to decrypt installation IDs
IID_KEY = b'\x6A\xC8\x5E\xD4'
#"""
# minimal quadratic non-residue of p
mqnr = least_quadratic_nonresidue(p)
# Galois field of order p
Fp = GF(p)
# Polynomial field Fp[x] over Fp
Fpx.<x> = Fp[]
# Hyperellptic curve function
F = sum(k*x^i for i, k in enumerate(coeffs))
# Hyperelliptic curve E: y^2 = F(x) over Fp
E = HyperellipticCurve(F)
# The jacobian over E
J = E.jacobian()In [ ]:
import hashlib
# Validate installation ID checksum
def validate_cksum(n):
print("Checksumming installation ID...")
n = n.replace("-", "")
cksum = 0
for i, k in enumerate(map(int, n)):
if (i + 1) % 6 == 0 or i == len(n) - 1:
print("Expected last digit", cksum % 7, "got", k)
if cksum % 7 != k:
return None
cksum = 0
else:
cksum += k * (i % 2 + 1)
parts = [n[i:i+5] for i in range(0, len(n), 6)]
n_out = "".join(parts)
if len(n_out) == 42:
n_out = n_out[:-1]
if len(n_out) != 45 and len(n_out) != 41:
return None
return int(n_out)
# Insert checksum digits into confirmation ID
def add_cksum(n):
cksums = []
n = str(n).zfill(35)
parts = [n[i:i+5] for i in range(0, len(n), 5)]
for p in parts:
cksum = 0
for i, k in enumerate(map(int, p)):
cksum += k * (i % 2 + 1)
cksums.append(str(cksum % 7))
n_out = ""
for i in range(7):
n_out += parts[i] + cksums[i] + ("-" if i != 6 else "")
return n_out
def encrypt(decrypted, key):
size_half = len(decrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = decrypted[size_half*2:]
decrypted = decrypted[:size_half*2]
for i in range(4):
first = decrypted[:size_half]
second = decrypted[size_half:]
sha1_result = hashlib.sha1(second + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
decrypted = second + bytes(x^^y for x,y in zip(first, sha1_result))
return decrypted + last
def decrypt(encrypted, key):
size_half = len(encrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = encrypted[size_half*2:]
encrypted = encrypted[:size_half*2]
for i in range(4):
first = encrypted[:size_half]
second = encrypted[size_half:]
sha1_result = hashlib.sha1(first + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first
return encrypted + last
# Find v of divisor (u, v) of curve y^2 = F(x)
def find_v(u):
f = F % u
c2 = u[1]^2 - 4 * u[0]
c1 = 2 * f[0] - f[1] * u[1]
if c2 == 0:
if c1 == 0:
return None
try:
v1 = sqrt(f[1]^2 / (2 * c1))
v1.lift()
except:
return None
else:
try:
d = 2 * sqrt(f[0]^2 + f[1] * (f[1] * u[0] - f[0] * u[1]))
v1_1 = sqrt((c1 - d)/c2)
v1_2 = sqrt((c1 + d)/c2)
except:
return None
try:
v1_1.lift()
v1 = v1_1
except:
try:
v1_2.lift()
v1 = v1_2
except:
return None
v0 = (f[1] + u[1] * v1^2) / (2 * v1)
v = v0 + v1 * x
assert (v^2 - f) % u == 0
return v
# unpack&decrypt installationId
installationId = validate_cksum(input("Installation ID (dashes optional): "))
# installationId = 11234597509478704096883784033789146715149
print(installationId)
if not installationId:
raise Exception("Invalid Installation ID (checksum fail)")
installationIdSize = 19 if len(str(installationId)) > 41 else 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))
iid = int(installationId).to_bytes(installationIdSize, byteorder='little')
iid = decrypt(iid, IID_KEY)
hwid = iid[:8]
productid = int.from_bytes(iid[8:17], byteorder='little')
productkeyhash = iid[17:]
pid1 = productid & ((1 << 17) - 1)
pid2 = (productid >> 17) & ((1 << 10) - 1)
pid3 = (productid >> 27) & ((1 << 24) - 1)
version = (productid >> 52) & 7
pid4 = productid >> 55
if version != (4 if len(iid) == 17 else 5):
print(f"Invalid Installation ID (unknown version {version})")
print(installationIdSize)
print(pid1, pid2, pid3, pid4)
key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')
data = [0x00] * 14
# data = b'\xb9g\xdd\xe1\xb0\xef-\x1e\xbd\x0frE\xd8\xbe'
print("\nConfirmation IDs:")
for i in range(0x81):
data[4] = i
# Encrypt conf ID, find u of divisor (u, v)
encrypted = encrypt(bytes(data), key)
encrypted = int.from_bytes(encrypted, byteorder="little")
x1, x2 = Fp(encrypted % p), Fp((encrypted // p) + 1)
u1, u0 = x1 * 2, (x1 ^ 2) - ((x2 ^ 2) * mqnr)
u = x^2 + u1 * x + u0
# Generate original divisor
v = find_v(u)
if not v:
continue
d2 = J(u, v)
divisor = d2 * INV
# Get x1 and x2
roots = [x for x, y in divisor[0].roots()]
if len(roots) > 0:
y = [divisor[1](r) for r in roots]
x1 = (-roots[0]).lift()
x2 = (-roots[1]).lift()
if (x1 > x2) or (y[0].lift() % 2 != y[1].lift() % 2):
x1 = (-roots[1]).lift()
x2 = (-roots[0]).lift()
else:
x2 = (divisor[0][1] / 2).lift()
x1 = sqrt((x2^2 - divisor[0][0]) / mqnr).lift() + p
# Win
conf_id = x1 * (p + 1) + x2
conf_id = add_cksum(conf_id)
print(conf_id)In [ ]:
import hashlib
# 226512-274743-842923-777124-961370-722240-570042-517722-757426
installationId = 44706039542602728435285810860722693781
installationIdSize = 17 # 17 for XP Gold, 19 for SP1+ (includes 12 bits of sha1(product key))
# all three of following are valid generated
# 013705-060122-603141-961392-086136-909901-494476
confid = input("Confirmation ID: ").replace("-", "")
confirmationId = int("".join([confid[i:i+5] for i in range(0, len(confid), 6)]))
# confirmationId = 13009861034010972507754924748629391
print(confirmationId)
# 022032-220754-159721-909624-985141-504586-914001
#confirmationId = 2203220751597290962985145045891400
# 137616-847280-708585-827476-874935-313366-790880
#confirmationId = 13761847287085882747874933133679088
def decrypt(encrypted, key):
size_half = len(encrypted) // 2
size_half_dwords = size_half - (size_half % 4)
last = encrypted[size_half*2:]
encrypted = encrypted[:size_half*2]
for i in range(4):
first = encrypted[:size_half]
second = encrypted[size_half:]
sha1_result = hashlib.sha1(first + key).digest()
sha1_result = (sha1_result[:size_half_dwords] +
sha1_result[size_half_dwords+4-(size_half%4) : size_half+4-(size_half%4)])
encrypted = bytes(x^^y for x,y in zip(second, sha1_result)) + first
return encrypted + last
# unpack&decrypt installationId
iid = int(installationId).to_bytes(installationIdSize, byteorder='little')
iid = decrypt(iid, IID_KEY)
hwid = iid[:8]
productid = int.from_bytes(iid[8:17], byteorder='little')
# productkeyhash is not used for validation, it exists just to allow the activation server to reject keygenned pids
productkeyhash = iid[17:]
pid1 = productid & ((1 << 17) - 1)
pid2 = (productid >> 17) & ((1 << 10) - 1)
pid3 = (productid >> 27) & ((1 << 24) - 1)
version = (productid >> 52) & 7
pid4 = productid >> 55
print(pid1, pid2, pid3, pid4)
if version != (4 if len(iid) == 17 else 5):
print(version)
key = hwid + int((pid1 << 41 | pid2 << 58 | pid3 << 17 | pid4) & ((1 << 64) - 1)).to_bytes(8, byteorder='little')
# deserialize divisor
x1 = confirmationId // (p + 1)
x2 = confirmationId % (p + 1)
if x1 <= p:
# two or less points over GF(p)
point1 = E.lift_x(Fp(-x1)) if x1 != p else None
point2 = E.lift_x(Fp(-x2)) if x2 != p else None
if point1 is not None and point2 is not None:
# there are 4 variants of how lift_x() could select both y-s
# we don't distinguish D and -D, but this still leaves 2 variants
# the chosen one is encoded by order of x1 <=> x2
lastbit1 = point1[1].lift() & 1
lastbit2 = point2[1].lift() & 1
if x2 < x1:
if lastbit1 == lastbit2:
point2 = E(point2[0], -point2[1])
else:
if lastbit1 != lastbit2:
point2 = E(point2[0], -point2[1])
point1 = J(point1) if point1 is not None else J(0)
point2 = J(point2) if point2 is not None else J(0)
divisor = point1 + point2
else:
# a pair of conjugate points over GF(p^2)
f = (x+x2)*(x+x2) - mqnr*x1*x1 # 43 is the minimal quadratic non-residue in Fp
Fp2 = GF(p^2)
point1 = E.lift_x(f.roots(Fp2)[0][0])
point2 = E(Fp2)(point1[0].conjugate(), point1[1].conjugate())
divisor = J(Fp2)(point1) + J(Fp2)(point2)
divisor = J(Fpx(divisor[0]), Fpx(divisor[1])) #return from Fp2 to Fp
d2 = divisor * 0x10001
assert d2[0].degree() == 2
x1 = d2[0][1]/2
x2 = sqrt((x1*x1-d2[0][0])/mqnr)
encrypted = x1.lift() + (x2.lift() - 1) * p
encrypted = int(encrypted).to_bytes(14,byteorder='little')
# end of the math
decrypted = decrypt(encrypted, key)
print(decrypted.hex())
# 0000000000000001000000000000 for the first confirmationId
# 0000000000000002000000000000 for the second confirmationId
# 0000000000000006000000000000 for the last confirmationId
assert decrypted[8:] == b'\0' * 6
assert decrypted[7] <= 0x80
# all zeroes in decrypted[0:7] are okay for the checker
# more precisely: if decrypted[6] == 0, first 6 bytes can be anything
# otherwise, decrypted[0] = length, and decrypted[1:1+length] must match first length bytes of sha1(product key)In [ ]: