class KeyData:
def __init__(self, seed1, seed2, seed3, password):
self.HdkSeed1 = seed1 & 0xFFFFFFFF
self.HdkSeed2 = seed2 & 0xFFFFFFFF
self.HdkSeed3 = seed3 & 0xFFFFFFFF
self.password = password & 0xFFFFFFFFFFFFFFFF
class HLCodeStruct:
def __init__(self):
self.gSeedArray = [0] * 4
self.gPtrArray = [0] * 16
self.gVar1 = 0
self.gVar2 = 0
self.gVar4 = 0
def init_g_seeds(key, t):
t.gSeedArray[0] = (key.HdkSeed3 >> 12) & 0xF
t.gSeedArray[1] = (key.HdkSeed3 >> 8) & 0xF
t.gSeedArray[2] = (key.HdkSeed3 >> 4) & 0xF
t.gSeedArray[3] = key.HdkSeed3 & 0xF
for i in range(16):
t.gPtrArray[i] = (((key.HdkSeed1 >> i) & 1) << 1) | ((key.HdkSeed2 >> i) & 1)
def init_g_var12(t):
t.gVar1 = 0xF
t.gVar2 = 0
t.gVar4 = 0 # Явно сбрасываем — безопаснее
def set_dongle_data(data, t, key):
data &= 0xF
tmp_ptr = t.gPtrArray[t.gVar1]
t.gVar4 = ((t.gVar4 << 4) | tmp_ptr) & 0xFFFF
t.gVar1 ^= data
t.gVar1 ^= t.gVar2
t.gVar1 ^= t.gSeedArray[tmp_ptr]
t.gVar2 = (((t.gVar2 << 2) + tmp_ptr) >> 1) & 0xF
def transform0_hw(w0, retw, t, key):
for _ in range(4):
for _ in range(4):
set_dongle_data((w0 & 0xFF) >> 2, t, key)
if w0 & 0x8000:
w0 = ((w0 << 1) | 1) & 0xFFFF
else:
w0 = (w0 << 1) & 0xFFFF
retw >>= 1
nb = (t.gVar4 >> 12) & 1
if nb == 0:
retw |= 0x8000
return retw & 0xFFFF
def transform0(w3, w4, t, key):
init_g_var12(t)
ax = w4 ^ transform0_hw(w4, transform0_hw(w3, 0, t, key), t, key)
ax = ((ax >> 15) | (ax << 1)) & 0xFFFF
lo = ax & 0xFF
hi = (ax >> 8) & 0xFF
lo = (lo + hi) & 0xFF
return (hi << 8) | lo
def hl_crypt(key, response: bytearray):
t = HLCodeStruct()
init_g_seeds(key, t)
w1 = int.from_bytes(response[0:2], 'little')
w2 = int.from_bytes(response[2:4], 'little')
w3 = int.from_bytes(response[4:6], 'little')
w4 = int.from_bytes(response[6:8], 'little')
for _ in range(5):
transf = w3 ^ w4 ^ transform0(w3, w4, t, key)
tmp1 = ((transf >> 8) & 0xFF) + (transf & 0xFF)
tmp1 = (tmp1 * 2 + (tmp1 >> 7)) & 0xFF
tmp1 = (tmp1 + 1) & 0xFF
tmp1 = (tmp1 * 2 + (tmp1 >> 7)) & 0xFF
tmp2 = (transf & 0xFF) + tmp1
tmp2 = (tmp2 * 2 + (tmp2 >> 7)) & 0xFF
tmp2 = (tmp2 * 2 + (tmp2 >> 7)) & 0xFF
transf = ((tmp1 << 8) | tmp2) & 0xFFFF
new_w4 = (transf + w4) & 0xFFFF
new_w4 = (((new_w4 + 1) & 0xFF) | (new_w4 & 0xFF00)) & 0xFFFF
new_w4 = ((new_w4 >> 15) | (new_w4 << 1)) & 0xFFFF
new_w4 = ((new_w4 >> 15) | (new_w4 << 1)) & 0xFFFF
transf ^= w1
new_w4 ^= w2
w1, w2 = w3, w4
w3 ^= transf
w4 ^= new_w4
# КРИТИЧЕСКАЯ ПЕРЕСТАНОВКА (как в оригинальном C-коде)
response[0:2] = w3.to_bytes(2, 'little')
response[2:4] = w4.to_bytes(2, 'little')
response[4:6] = w1.to_bytes(2, 'little')
response[6:8] = w2.to_bytes(2, 'little')
# Тест
key = KeyData(
seed1=35320,
seed2=12180,
seed3=27464,
password=0x536F667457617265
)
buf = bytearray.fromhex("53 6F 66 74 57 61 72 65")
print("IN :", buf.hex())
hl_crypt(key, buf)
print("OUT:", buf.hex())
Y2xhc3MgS2V5RGF0YToKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBzZWVkMSwgc2VlZDIsIHNlZWQzLCBwYXNzd29yZCk6CiAgICAgICAgc2VsZi5IZGtTZWVkMSA9IHNlZWQxICYgMHhGRkZGRkZGRgogICAgICAgIHNlbGYuSGRrU2VlZDIgPSBzZWVkMiAmIDB4RkZGRkZGRkYKICAgICAgICBzZWxmLkhka1NlZWQzID0gc2VlZDMgJiAweEZGRkZGRkZGCiAgICAgICAgc2VsZi5wYXNzd29yZCA9IHBhc3N3b3JkICYgMHhGRkZGRkZGRkZGRkZGRkZGCgpjbGFzcyBITENvZGVTdHJ1Y3Q6CiAgICBkZWYgX19pbml0X18oc2VsZik6CiAgICAgICAgc2VsZi5nU2VlZEFycmF5ID0gWzBdICogNAogICAgICAgIHNlbGYuZ1B0ckFycmF5ID0gWzBdICogMTYKICAgICAgICBzZWxmLmdWYXIxID0gMAogICAgICAgIHNlbGYuZ1ZhcjIgPSAwCiAgICAgICAgc2VsZi5nVmFyNCA9IDAKCmRlZiBpbml0X2dfc2VlZHMoa2V5LCB0KToKICAgIHQuZ1NlZWRBcnJheVswXSA9IChrZXkuSGRrU2VlZDMgPj4gMTIpICYgMHhGCiAgICB0LmdTZWVkQXJyYXlbMV0gPSAoa2V5Lkhka1NlZWQzID4+IDgpICYgMHhGCiAgICB0LmdTZWVkQXJyYXlbMl0gPSAoa2V5Lkhka1NlZWQzID4+IDQpICYgMHhGCiAgICB0LmdTZWVkQXJyYXlbM10gPSBrZXkuSGRrU2VlZDMgJiAweEYKICAgIGZvciBpIGluIHJhbmdlKDE2KToKICAgICAgICB0LmdQdHJBcnJheVtpXSA9ICgoKGtleS5IZGtTZWVkMSA+PiBpKSAmIDEpIDw8IDEpIHwgKChrZXkuSGRrU2VlZDIgPj4gaSkgJiAxKQoKZGVmIGluaXRfZ192YXIxMih0KToKICAgIHQuZ1ZhcjEgPSAweEYKICAgIHQuZ1ZhcjIgPSAwCiAgICB0LmdWYXI0ID0gMCAgIyDQr9Cy0L3QviDRgdCx0YDQsNGB0YvQstCw0LXQvCDigJQg0LHQtdC30L7Qv9Cw0YHQvdC10LUKCmRlZiBzZXRfZG9uZ2xlX2RhdGEoZGF0YSwgdCwga2V5KToKICAgIGRhdGEgJj0gMHhGCiAgICB0bXBfcHRyID0gdC5nUHRyQXJyYXlbdC5nVmFyMV0KICAgIHQuZ1ZhcjQgPSAoKHQuZ1ZhcjQgPDwgNCkgfCB0bXBfcHRyKSAmIDB4RkZGRgogICAgdC5nVmFyMSBePSBkYXRhCiAgICB0LmdWYXIxIF49IHQuZ1ZhcjIKICAgIHQuZ1ZhcjEgXj0gdC5nU2VlZEFycmF5W3RtcF9wdHJdCiAgICB0LmdWYXIyID0gKCgodC5nVmFyMiA8PCAyKSArIHRtcF9wdHIpID4+IDEpICYgMHhGCgpkZWYgdHJhbnNmb3JtMF9odyh3MCwgcmV0dywgdCwga2V5KToKICAgIGZvciBfIGluIHJhbmdlKDQpOgogICAgICAgIGZvciBfIGluIHJhbmdlKDQpOgogICAgICAgICAgICBzZXRfZG9uZ2xlX2RhdGEoKHcwICYgMHhGRikgPj4gMiwgdCwga2V5KQogICAgICAgICAgICBpZiB3MCAmIDB4ODAwMDoKICAgICAgICAgICAgICAgIHcwID0gKCh3MCA8PCAxKSB8IDEpICYgMHhGRkZGCiAgICAgICAgICAgIGVsc2U6CiAgICAgICAgICAgICAgICB3MCA9ICh3MCA8PCAxKSAmIDB4RkZGRgogICAgICAgIHJldHcgPj49IDEKICAgICAgICBuYiA9ICh0LmdWYXI0ID4+IDEyKSAmIDEKICAgICAgICBpZiBuYiA9PSAwOgogICAgICAgICAgICByZXR3IHw9IDB4ODAwMAogICAgcmV0dXJuIHJldHcgJiAweEZGRkYKCmRlZiB0cmFuc2Zvcm0wKHczLCB3NCwgdCwga2V5KToKICAgIGluaXRfZ192YXIxMih0KQogICAgYXggPSB3NCBeIHRyYW5zZm9ybTBfaHcodzQsIHRyYW5zZm9ybTBfaHcodzMsIDAsIHQsIGtleSksIHQsIGtleSkKICAgIGF4ID0gKChheCA+PiAxNSkgfCAoYXggPDwgMSkpICYgMHhGRkZGCiAgICBsbyA9IGF4ICYgMHhGRgogICAgaGkgPSAoYXggPj4gOCkgJiAweEZGCiAgICBsbyA9IChsbyArIGhpKSAmIDB4RkYKICAgIHJldHVybiAoaGkgPDwgOCkgfCBsbwoKZGVmIGhsX2NyeXB0KGtleSwgcmVzcG9uc2U6IGJ5dGVhcnJheSk6CiAgICB0ID0gSExDb2RlU3RydWN0KCkKICAgIGluaXRfZ19zZWVkcyhrZXksIHQpCgogICAgdzEgPSBpbnQuZnJvbV9ieXRlcyhyZXNwb25zZVswOjJdLCAnbGl0dGxlJykKICAgIHcyID0gaW50LmZyb21fYnl0ZXMocmVzcG9uc2VbMjo0XSwgJ2xpdHRsZScpCiAgICB3MyA9IGludC5mcm9tX2J5dGVzKHJlc3BvbnNlWzQ6Nl0sICdsaXR0bGUnKQogICAgdzQgPSBpbnQuZnJvbV9ieXRlcyhyZXNwb25zZVs2OjhdLCAnbGl0dGxlJykKCiAgICBmb3IgXyBpbiByYW5nZSg1KToKICAgICAgICB0cmFuc2YgPSB3MyBeIHc0IF4gdHJhbnNmb3JtMCh3MywgdzQsIHQsIGtleSkKICAgICAgICB0bXAxID0gKCh0cmFuc2YgPj4gOCkgJiAweEZGKSArICh0cmFuc2YgJiAweEZGKQogICAgICAgIHRtcDEgPSAodG1wMSAqIDIgKyAodG1wMSA+PiA3KSkgJiAweEZGCiAgICAgICAgdG1wMSA9ICh0bXAxICsgMSkgJiAweEZGCiAgICAgICAgdG1wMSA9ICh0bXAxICogMiArICh0bXAxID4+IDcpKSAmIDB4RkYKCiAgICAgICAgdG1wMiA9ICh0cmFuc2YgJiAweEZGKSArIHRtcDEKICAgICAgICB0bXAyID0gKHRtcDIgKiAyICsgKHRtcDIgPj4gNykpICYgMHhGRgogICAgICAgIHRtcDIgPSAodG1wMiAqIDIgKyAodG1wMiA+PiA3KSkgJiAweEZGCgogICAgICAgIHRyYW5zZiA9ICgodG1wMSA8PCA4KSB8IHRtcDIpICYgMHhGRkZGCgogICAgICAgIG5ld193NCA9ICh0cmFuc2YgKyB3NCkgJiAweEZGRkYKICAgICAgICBuZXdfdzQgPSAoKChuZXdfdzQgKyAxKSAmIDB4RkYpIHwgKG5ld193NCAmIDB4RkYwMCkpICYgMHhGRkZGCiAgICAgICAgbmV3X3c0ID0gKChuZXdfdzQgPj4gMTUpIHwgKG5ld193NCA8PCAxKSkgJiAweEZGRkYKICAgICAgICBuZXdfdzQgPSAoKG5ld193NCA+PiAxNSkgfCAobmV3X3c0IDw8IDEpKSAmIDB4RkZGRgoKICAgICAgICB0cmFuc2YgXj0gdzEKICAgICAgICBuZXdfdzQgXj0gdzIKCiAgICAgICAgdzEsIHcyID0gdzMsIHc0CiAgICAgICAgdzMgXj0gdHJhbnNmCiAgICAgICAgdzQgXj0gbmV3X3c0CgogICAgIyDQmtCg0JjQotCY0KfQldCh0JrQkNCvINCf0JXQoNCV0KHQotCQ0J3QntCS0JrQkCAo0LrQsNC6INCyINC+0YDQuNCz0LjQvdCw0LvRjNC90L7QvCBDLdC60L7QtNC1KQogICAgcmVzcG9uc2VbMDoyXSA9IHczLnRvX2J5dGVzKDIsICdsaXR0bGUnKQogICAgcmVzcG9uc2VbMjo0XSA9IHc0LnRvX2J5dGVzKDIsICdsaXR0bGUnKQogICAgcmVzcG9uc2VbNDo2XSA9IHcxLnRvX2J5dGVzKDIsICdsaXR0bGUnKQogICAgcmVzcG9uc2VbNjo4XSA9IHcyLnRvX2J5dGVzKDIsICdsaXR0bGUnKQoKIyDQotC10YHRggprZXkgPSBLZXlEYXRhKAogICAgc2VlZDE9MzUzMjAsCiAgICBzZWVkMj0xMjE4MCwKICAgIHNlZWQzPTI3NDY0LAogICAgcGFzc3dvcmQ9MHg1MzZGNjY3NDU3NjE3MjY1CikKCmJ1ZiA9IGJ5dGVhcnJheS5mcm9taGV4KCI1MyA2RiA2NiA3NCA1NyA2MSA3MiA2NSIpCnByaW50KCJJTiA6IiwgYnVmLmhleCgpKQpobF9jcnlwdChrZXksIGJ1ZikKcHJpbnQoIk9VVDoiLCBidWYuaGV4KCkp