Module refinery.lib.unquarantine

Implements extraction logic for anti-virus quarantine containers. Each handler function accepts a memoryview of the quarantine file and returns a QuarantineResult on success or None on failure. The public entry point is unquarantine().

This library is largely untested. Acquiring test samples is difficult. If you have atest samples, please let me know.

Expand source code Browse git
"""
Implements extraction logic for anti-virus quarantine containers. Each handler function accepts a
memoryview of the quarantine file and returns a `refinery.lib.unquarantine.QuarantineResult` on
success or `None` on failure. The public entry point is `refinery.lib.unquarantine.unquarantine`.

This library is largely untested. Acquiring test samples is difficult. If you have atest samples,
please let me know.
"""
from __future__ import annotations

import enum
import struct
import zlib

from typing import NamedTuple


class QuarantineResult(NamedTuple):
    data: bytes | bytearray
    vendor: str
    filename: str | None = None
    threat: str | None = None


def _rc4(key: bytes | bytearray, data: bytes | bytearray | memoryview) -> bytearray:
    from Cryptodome.Cipher import ARC4
    return bytearray(ARC4.new(key).encrypt(bytes(data)))


def _blowfish_ecb(key: bytes | bytearray, data: bytes | bytearray | memoryview) -> bytearray:
    from Cryptodome.Cipher import Blowfish
    cipher = Blowfish.new(key, Blowfish.MODE_ECB)
    raw = bytes(data)
    pad = 8 - len(raw) % 8
    if pad < 8:
        raw += b'\x00' * pad
    return bytearray(cipher.decrypt(raw))[:len(data)]


def _blowfish_ecb_swap(
    key: bytes | bytearray,
    data: bytes | bytearray | memoryview,
) -> bytearray:
    from Cryptodome.Cipher import Blowfish
    cipher = Blowfish.new(key, Blowfish.MODE_ECB)
    raw = bytes(data)
    pad = 8 - len(raw) % 8
    if pad < 8:
        raw += b'\x00' * pad
    out = bytearray()
    for k in range(0, len(raw), 8):
        block = raw[k:k + 8]
        a = struct.pack('>I', struct.unpack('<I', block[0:4])[0])
        b = struct.pack('>I', struct.unpack('<I', block[4:8])[0])
        dec = cipher.decrypt(a + b)
        a = struct.pack('>I', struct.unpack('<I', dec[0:4])[0])
        b = struct.pack('>I', struct.unpack('<I', dec[4:8])[0])
        out.extend(a + b)
    return out[:len(data)]


def _md5(data: bytes) -> bytes:
    from Cryptodome.Hash import MD5
    return MD5.new(data).digest()


def _u16(data: bytes | bytearray | memoryview, offset: int = 0) -> int:
    return struct.unpack_from('<H', data, offset)[0]


def _u32(data: bytes | bytearray | memoryview, offset: int = 0) -> int:
    return struct.unpack_from('<I', data, offset)[0]


def _u64(data: bytes | bytearray | memoryview, offset: int = 0) -> int:
    return struct.unpack_from('<Q', data, offset)[0]


def _xor_byte(data: bytes | bytearray | memoryview, key: int) -> bytearray:
    out = bytearray(data)
    for i in range(len(out)):
        out[i] ^= key
    return out


def _xor_cyclic(
    data: bytes | bytearray | memoryview,
    key: bytes | bytearray,
) -> bytearray:
    klen = len(key)
    out = bytearray(data)
    for i in range(len(out)):
        out[i] ^= key[i % klen]
    return out


_KEY_V3B = bytes(b ^ 0xFF for b in b'v3backup!@#$%^&)')

_KEY_AVAST = bytes([
    0x33, 0xB6, 0x59, 0x83, 0x8B, 0x43, 0x75, 0xFB, 0x35, 0xB6, 0x8A, 0x37,
    0xAE, 0x29, 0x16, 0x47, 0xA2, 0x51, 0x41, 0x4F, 0x69, 0x9A, 0x07, 0xF5,
    0xF1, 0x69, 0x80, 0x89, 0x60, 0x15, 0x8E, 0xF6, 0xB2, 0x3B, 0x89, 0xC4,
    0x9F, 0xFF, 0x65, 0x2E, 0x36, 0xD3, 0xF2, 0x10, 0xEA, 0x76, 0x88, 0xAD,
    0x19, 0x39, 0x44, 0xEF, 0x7E, 0xBC, 0xAF, 0xA0, 0x26, 0x7D, 0x83, 0xC9,
    0x13, 0xC7, 0xBD, 0xE1, 0x16, 0xEB, 0x27, 0x69, 0x2C, 0x17, 0xE2, 0xF9,
    0xF8, 0x8A, 0x7F, 0x6E, 0x6F, 0xEB, 0x16, 0x16, 0x60, 0x48, 0x86, 0x12,
    0xC5, 0x9A, 0x91, 0x6B, 0xB3, 0xA2, 0x71, 0x38, 0xC6, 0x2F, 0x4E, 0x05,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0x0F, 0x08, 0xE3, 0x73, 0xA6, 0xA0, 0x2E, 0x02,
    0x76, 0x7F, 0x97, 0x9F, 0x8E, 0x5D, 0x80, 0xDE, 0x75, 0xDB, 0x41, 0x31,
    0x62, 0xCC, 0x68, 0x73, 0x79, 0x33, 0x3F, 0xE8, 0xDC, 0xCD, 0xF5, 0x9A,
    0x9E, 0x1F, 0x21, 0xD7, 0x97, 0xDF, 0x66, 0xC8, 0x50, 0x0F, 0xBD, 0x2E,
    0x35, 0x11, 0x1D, 0x77, 0xE1, 0x62, 0xA1, 0xCA, 0x4C, 0xC7, 0x4C, 0xE3,
    0xB5, 0x5C, 0x86, 0xD5, 0xE4, 0xCE, 0xF5, 0xD3, 0xCF, 0xA5, 0xE6, 0x54,
    0xA7, 0x2E, 0x7B, 0xA8, 0xBA, 0xA5, 0x8B, 0x02, 0x15, 0x4E, 0xEE, 0xD6,
    0xB1, 0xE4, 0xEB, 0x46, 0x9B, 0x8B, 0xB5, 0x26, 0xCA, 0x88, 0xAF, 0xE6,
    0xF8, 0x56, 0xFA, 0x6F, 0x39, 0x48, 0x6B, 0xFA, 0xF0, 0x7A, 0x4F, 0xC4,
    0xE3, 0xA7, 0x2C, 0x62, 0x44, 0x84, 0x39, 0xE3, 0xDD, 0xED, 0xA4, 0xF6,
    0xFD, 0x4E, 0xB8, 0x92, 0x0C, 0x1D, 0x3A, 0x78, 0x7E, 0xDD, 0x03, 0x3E,
    0xD1, 0x7B, 0xE6, 0x2C, 0xBE, 0xD3, 0x87, 0x75, 0xD5, 0xE1, 0x2F, 0x07,
    0x19, 0x37, 0x01, 0x40, 0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2,
    0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F, 0x6F, 0x43, 0x73, 0x1E,
    0x13, 0x7D, 0x30, 0x3D, 0xFA, 0x30, 0x5B, 0x81, 0x68, 0x7C, 0xF9, 0xEA,
    0x52, 0xA9, 0xE3, 0xF4, 0x28, 0x8C, 0x01, 0x38, 0xAF, 0xE9, 0xD0, 0xA8,
    0x2C, 0xD4, 0x62, 0xE8, 0x41, 0xA5, 0xB1, 0x71, 0xC1, 0x2E, 0x2B, 0x79,
    0xE3, 0xFF, 0xA8, 0x24, 0x12, 0xAF, 0x89, 0xA7, 0x9A, 0x6D, 0x73, 0xE6,
    0xCD, 0xE8, 0x11, 0x75, 0xFF, 0xE6, 0x70, 0x8A, 0x8A, 0xE5, 0x4F, 0x08,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0x32, 0x91, 0x88, 0xC4, 0x01, 0x19, 0x08, 0x2F,
    0x48, 0x84, 0xD2, 0x30, 0x5F, 0x56, 0xB7, 0x15, 0xAF, 0x90, 0x52, 0x1A,
    0x1B, 0x16, 0xCD, 0x84, 0xEA, 0x6E, 0xE6, 0x9A, 0x89, 0xC3, 0x48, 0xF0,
    0x9C, 0xBD, 0xC3, 0x95, 0x18, 0xF9, 0x30, 0xC0, 0xD2, 0x50, 0x21, 0x66,
    0x41, 0xA1, 0x82, 0xB6, 0xA5, 0x57, 0x64, 0x6A, 0x96, 0x96, 0x8C, 0x1D,
    0x94, 0x74, 0xD6, 0x2F, 0x77, 0x12, 0x4B, 0x3F, 0xF6, 0x5C, 0x33, 0x8C,
    0xA1, 0x21, 0x7F, 0x3C, 0x5B, 0x9D, 0xCB, 0x2C, 0x1F, 0x6E, 0xA0, 0xAA,
    0x8F, 0xEA, 0x09, 0x90, 0x4B, 0x47, 0xD0, 0x27, 0xA7, 0x9A, 0x89, 0x78,
    0x9E, 0x20, 0xF2, 0x8E, 0x81, 0x12, 0xDD, 0x09, 0xDF, 0x0D, 0x7F, 0x4B,
    0x76, 0xB5, 0x89, 0xA7, 0x69, 0x7F, 0x42, 0x15, 0x22, 0x7F, 0xE9, 0x59,
    0xA6, 0xFD, 0x59, 0x2A, 0x8E, 0x58, 0x25, 0xCC, 0xFC, 0x63, 0x88, 0xC5,
    0x64, 0x22, 0x71, 0x48, 0x2E, 0xC9, 0xC8, 0xE3, 0xAE, 0xB4, 0x94, 0xBD,
    0x73, 0x7A, 0x11, 0x04, 0x37, 0x0C, 0xF0, 0x04, 0x1D, 0x57, 0x62, 0x6F,
    0x65, 0xFE, 0x57, 0x97, 0xBA, 0x44, 0x81, 0xA7, 0x9D, 0xB4, 0xFC, 0x16,
    0x3E, 0xB5, 0x05, 0xED, 0xD1, 0x67, 0xD5, 0x2A, 0xE7, 0xCF, 0xD5, 0xD5,
    0xD4, 0xC8, 0x4A, 0x1F, 0x85, 0x39, 0x79, 0xDF, 0xB7, 0x1E, 0x79, 0x75,
    0xC3, 0xB3, 0xB4, 0xCA, 0xA1, 0x3D, 0x38, 0x54, 0xC6, 0x42, 0x17, 0x3B,
    0x57, 0x02, 0xB9, 0xDC, 0xDE, 0xC8, 0x1F, 0x88, 0x0A, 0x9B, 0xB7, 0x4E,
    0x95, 0x25, 0xCE, 0xC8, 0x3F, 0x02, 0x74, 0xFD, 0xB8, 0xA2, 0x5C, 0x12,
    0x1D, 0xD6, 0x25, 0x37, 0xE3, 0xD3, 0x6C, 0x2A, 0x9D, 0x8F, 0x93, 0x26,
    0xE3, 0x3F, 0xC1, 0xD6, 0xBE, 0x14, 0x8E, 0xEF, 0x08, 0xBC, 0x4D, 0xB4,
    0xDE, 0xE9, 0x05, 0x14, 0x29, 0xA0, 0x3A, 0x57, 0xEB, 0x52, 0x47, 0xD7,
    0xDE, 0x1D, 0x42, 0x97, 0x51, 0xD3, 0x85, 0x0D, 0xA7, 0xF0, 0xF7, 0xD3,
    0x30, 0xFC, 0x78, 0xC3, 0x77, 0x73, 0x8D, 0x8E, 0xF4, 0x18, 0x2D, 0x33,
    0xC6, 0xE9, 0x9F, 0xE3, 0x1E, 0x81, 0x8A, 0x99, 0xDB, 0x26, 0x36, 0x8D,
    0xC1, 0x9D, 0xC0, 0x03, 0xF2, 0xB7, 0xBC, 0xA3, 0xBD, 0x22, 0xA8, 0xAF,
    0x5D, 0x37, 0x1A, 0x3E, 0x1D, 0x9C, 0x48, 0xD6, 0xA8, 0x0D, 0x21, 0xA6,
    0x7A, 0xC0, 0x11, 0x37, 0xB3, 0x97, 0x5D, 0x50, 0x5A, 0xE8, 0xDC, 0xC6,
    0x7F, 0xF8, 0x16, 0x35, 0xED, 0x28, 0xEF, 0x51, 0xEE, 0x05, 0x43, 0x4F,
    0x62, 0x15, 0xBE, 0x8B, 0x88, 0x0F, 0x6C, 0xB0, 0x57, 0x69, 0x57, 0xB1,
    0xFF, 0x76, 0x44, 0x64, 0x6C, 0xA3, 0x12, 0x9C, 0x54, 0x5C, 0x7B, 0xA2,
    0xC3, 0xAD, 0x26, 0x60, 0xDE, 0xF0, 0x5D, 0x23, 0xA3, 0x28, 0xF5, 0xAB,
    0x88, 0x8B, 0x61, 0x85, 0x02, 0xC8, 0xB3, 0x1D, 0x41, 0xD2, 0x30, 0x0B,
    0x22, 0x2C, 0xEA, 0xD3, 0xC9, 0xEB, 0x3F, 0x23, 0x80, 0xDA, 0xC3, 0x84,
    0x0F, 0x6C, 0xD4, 0xCC, 0x1E, 0x5F, 0x6D, 0x22, 0xAC, 0x74, 0x1D, 0xD2,
    0x09, 0x3A, 0x1B, 0x28, 0x08, 0xB6, 0xED, 0x92, 0xE6, 0xA5, 0xAD, 0x9B,
    0x84, 0x09, 0x40, 0xA2, 0x91, 0x6C, 0x1A, 0x90, 0x72, 0x8E, 0x54, 0x63,
    0x16, 0xA6, 0xDD, 0xFC, 0xF3, 0x23, 0x3E, 0x11, 0xF0, 0x0A, 0x32, 0x2C,
    0x7B, 0x81, 0x60, 0xFF, 0x61, 0xF7, 0x4A, 0x7A, 0x07, 0x10, 0x7B, 0xEB,
    0x68, 0x90, 0x56, 0xD9, 0x6A, 0x3D, 0xDB, 0x22, 0x31, 0x65, 0xA2, 0x2D,
    0xA4, 0xB4, 0x82, 0xC3, 0xEA, 0x7C, 0xF8, 0x24, 0x88, 0x80, 0x4A, 0x2F,
    0xA9, 0x56, 0x55, 0xBD, 0x2E, 0x88, 0xF7, 0x26, 0x36, 0x1D, 0x18, 0x65,
    0xF9, 0xB7, 0xB3, 0x63, 0xB2, 0xE6, 0x0F, 0x76, 0x5A, 0xF4, 0x3A, 0x0A,
    0xB7, 0x6C, 0x11, 0x1F, 0x00, 0xCC, 0x31, 0x16, 0x5C, 0x6F, 0x51, 0x2C,
    0xC4, 0xD9, 0xEE, 0xF6, 0xC4, 0xF9, 0x2D, 0x81, 0x6E, 0xF1, 0xCD, 0x13,
    0x44, 0x77, 0x43, 0x67, 0xBD, 0xB6, 0x14, 0x91, 0xD9, 0x32, 0xB2, 0xFF,
    0x96, 0x46, 0x26, 0xC4, 0x62, 0x1E, 0x6C, 0xB4, 0x3A, 0xC5, 0xBC, 0xB8,
    0xEA, 0x40, 0x7B, 0x72, 0x7F, 0xF8, 0x82, 0xDC, 0x67, 0x47, 0xF2, 0x38,
    0x66, 0xC6, 0x73, 0xD5, 0xA9, 0x8F, 0x48, 0xE5, 0x10, 0x54, 0x7B, 0x43,
    0xFD, 0xEE, 0x9B, 0x1C, 0x12, 0x55, 0x15, 0x20, 0xF8, 0x23, 0x0F, 0x95,
    0x85, 0xCB, 0x5F, 0xF5, 0x1B, 0x57, 0x4F, 0x25, 0x0B, 0xCA, 0xA1, 0x6A,
    0x6D, 0xF2, 0x69, 0xD8, 0x8E, 0x40, 0xEC, 0x64, 0x60, 0x93, 0x43, 0x3A,
    0x01, 0x77, 0xBC, 0x4B, 0x72, 0x73, 0x23, 0xF5, 0xD1, 0x0C, 0x70, 0x1B,
    0x71, 0xE3, 0x95, 0xF9, 0x51, 0x7D, 0xAB, 0x40, 0x46, 0xB3, 0xEC, 0x38,
    0x2A, 0xFB, 0x9C, 0xB9, 0xA2, 0x69, 0xE4, 0x22, 0xBD, 0xB1, 0x6B, 0x08,
    0xA9, 0xE0, 0xE7, 0x66, 0x48, 0xF1, 0xB0, 0x2D, 0x6B, 0x53, 0x8B, 0x62,
    0x54, 0xD4, 0xE8, 0x48, 0xDA, 0xFB, 0xE0, 0x8C, 0x0B, 0x8A, 0x9D, 0xA4,
    0x46, 0x88, 0x5B, 0x47, 0xE5, 0x2A, 0xF6, 0x84, 0xB3, 0xAE, 0x31, 0xF6,
    0xC5, 0x3F, 0x11, 0x59, 0x40, 0x7C, 0x66, 0x74, 0x8D, 0xA3, 0x1A, 0x13,
    0x7E, 0xD6, 0x90, 0xCD, 0xC8, 0x97, 0xAF, 0x33, 0xBA, 0xAD, 0xF6, 0xDE,
    0x64, 0x41, 0xD3, 0xAE, 0x08, 0x00, 0xF6, 0x2F, 0x73, 0xD3, 0x21, 0x64,
    0x4E, 0xC5, 0x27, 0xFA, 0xB6, 0x62, 0x02, 0xB9, 0x37, 0xD2, 0x22, 0x9D,
    0xE6, 0x9F, 0x08, 0x47, 0xFB, 0x05, 0x1E, 0xB8, 0x2C, 0xE4, 0xC5, 0x92,
    0x91, 0xAA, 0x50, 0xA3, 0xE4, 0x78, 0x80, 0xC5, 0xB0, 0x14, 0x4F, 0x90,
    0x11, 0x4D, 0x80, 0xEB, 0x20, 0x25, 0x3C, 0x3F, 0x03, 0xF6, 0xFC, 0xFE,
    0xAE, 0xCD, 0x0F, 0x48, 0xF5, 0x90, 0xE2, 0x9E, 0xC1, 0x6C, 0xA3, 0x33,
    0xEB, 0xD9, 0xA4, 0xE9, 0x33, 0x0D, 0xE2, 0x5D, 0x4A, 0x48, 0xC9, 0xCE,
    0xF4, 0xDF, 0xE9, 0xD2, 0x8D, 0xDB, 0x3D, 0x2E, 0xE9, 0x0C, 0xBE, 0x8D,
    0x36, 0x4B, 0xD3, 0xA9, 0xA7, 0xC9, 0xE5, 0xB6, 0xFB, 0x83, 0x37, 0xD5,
    0x65, 0x31, 0x61, 0x7F, 0x30, 0xDC, 0x4B, 0xB6, 0x30, 0xCD, 0x44, 0xE0,
    0x9B, 0x07, 0x4E, 0x00, 0x89, 0x80, 0xAC, 0xC9, 0xFB, 0x86, 0x4E, 0x78,
    0xA2, 0x48, 0x17, 0x34, 0x67, 0x94, 0x11, 0x60, 0x67, 0xFC, 0x61, 0x74,
    0xC1, 0x6B, 0x40, 0x47, 0xA1, 0x22, 0x75, 0xCA, 0x56, 0x99, 0x0B, 0xBB,
    0x33, 0x07, 0xA2, 0x88, 0x78, 0xAE, 0xF6, 0x1B, 0x8D, 0x7E, 0xE8, 0x98,
    0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F,
    0x59, 0x40, 0xBA, 0x35, 0x0A, 0x34, 0x21, 0x3C, 0x8D, 0xDA, 0x9B, 0x9D,
    0x16, 0xCB, 0x22, 0x63, 0x27, 0xA4, 0x3A, 0xD1, 0x0E, 0xCB, 0x79, 0x87,
    0x34, 0x89, 0xE9, 0x8F, 0x9B, 0x0E, 0xC8, 0x05, 0x8D, 0x7E, 0xE8, 0x98,
    0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F,
    0xF4, 0x03, 0xFC, 0xBE, 0x12, 0xEB, 0xDA, 0x57, 0x28, 0x28, 0x26, 0x43,
    0xCF, 0xC4, 0x71, 0xBF, 0x60, 0x51, 0x33, 0xBF, 0x3D, 0xC1, 0xB6, 0xBB,
    0x18, 0x39, 0xCC, 0xE0, 0x8B, 0x66, 0x33, 0xC2, 0x82, 0x79, 0x3E, 0xFC,
    0x73, 0x7C, 0xD2, 0xD5, 0xF9, 0x7D, 0x83, 0xA6, 0x2C, 0x1B, 0x0B, 0x76,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0x2D, 0x72, 0x31, 0x49, 0xD9, 0x9F, 0xEB, 0x32,
    0x72, 0xDC, 0xE0, 0x59, 0xF8, 0xE2, 0xAA, 0xBB, 0x94, 0x48, 0xE3, 0x65,
    0x89, 0x2B, 0xB6, 0xA4, 0xCA, 0x38, 0x39, 0x82, 0x92, 0x1D, 0xED, 0x9E,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0xE3, 0xAB, 0xE2, 0x85, 0x50, 0x08, 0xD1, 0xA8,
    0x27, 0x88, 0x2D, 0x92, 0x65, 0x5D, 0x30, 0xBF, 0xA1, 0x61, 0x69, 0xA1,
    0x32, 0xAC, 0xC0, 0x68, 0x55, 0x5A, 0x82, 0x98, 0x2C, 0x2C, 0x02, 0x64,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2,
    0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F, 0xAD, 0xF6, 0x5C, 0x7E,
    0xC3, 0x7B, 0x8A, 0x5C, 0xC3, 0xF6, 0x40, 0x28, 0x0F, 0x4E, 0x30, 0x0F,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0xFF, 0x1D, 0x50, 0x0E, 0xFE, 0x67, 0xF3, 0x44,
    0x9C, 0xDF, 0xEE, 0xC2, 0x4A, 0xFB, 0xCA, 0x04, 0x96, 0x16, 0x10, 0xF1,
    0x30, 0x17, 0x4E, 0x7F, 0x4C, 0xF8, 0x86, 0x53, 0x7C, 0x73, 0x15, 0x0A,
    0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71,
    0xD3, 0xF6, 0x08, 0x0F, 0xF4, 0xAD, 0x23, 0x55, 0x42, 0x96, 0xED, 0xC0,
    0x71, 0x75, 0x9B, 0x1C, 0x70, 0x17, 0x07, 0x25, 0x0A, 0x83, 0x42, 0xEB,
    0x81, 0xAB, 0x4B, 0x1B, 0xE5, 0x39, 0xA0, 0x9E, 0x98, 0xD7, 0x33, 0x67,
    0x3E, 0xF0, 0xFA, 0xBB, 0xB7, 0xBD, 0x71, 0xD1, 0x4D, 0x11, 0x52, 0xC3,
    0x2B, 0x9C, 0xDA, 0x43, 0xA3, 0x48, 0xEB, 0x27, 0x04, 0x0E, 0x6E, 0x0D,
    0xF6, 0x42, 0xA5, 0xB7, 0xB3, 0xC5, 0x78, 0x45, 0xD8, 0x28, 0xCE, 0x52,
    0x22, 0xC5, 0x82, 0x0F, 0x1F, 0xA9, 0x0F, 0xCE, 0xD9, 0xD2, 0x1F, 0xA5,
    0x8A, 0xFE, 0x93, 0xBC, 0x02, 0x18, 0x2F, 0x7C, 0x27, 0x5C, 0x67, 0xC1,
    0xE0, 0xF6, 0xBD, 0x94, 0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2,
    0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F, 0x53, 0xF1, 0x82, 0xF9,
    0xDF, 0x59, 0xF3, 0x3C, 0xA8, 0xAF, 0x18, 0x68, 0xFF, 0xD8, 0x1B, 0x19,
    0x19, 0xCF, 0x42, 0xEB, 0xE2, 0xC5, 0x6B, 0x22, 0x90, 0x4A, 0x34, 0xE3,
    0x36, 0x8A, 0x49, 0xE1, 0xEA, 0x79, 0xFA, 0x1F, 0x79, 0x8A, 0x3C, 0xE3,
    0xC4, 0x8C, 0x46, 0x49, 0x56, 0x7B, 0x80, 0x8F, 0x8D, 0x7E, 0xE8, 0x98,
    0x30, 0xFF, 0x6F, 0xB2, 0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F,
    0xF9, 0x85, 0x6C, 0xC3, 0x6E, 0x9A, 0x3D, 0x9C, 0x3D, 0xAC, 0xD0, 0x9C,
    0x11, 0x8F, 0x82, 0x1C, 0x45, 0xC6, 0xF3, 0xE6, 0xD6, 0x12, 0x6A, 0xFC,
    0x3D, 0x61, 0x6C, 0xED, 0xBB, 0x7A, 0x08, 0xB5, 0x13, 0x9D, 0x43, 0x91,
    0x75, 0x69, 0x20, 0x4D, 0xE2, 0xBE, 0xA7, 0x30, 0x93, 0xF7, 0xA7, 0xC1,
    0x5A, 0x6C, 0x35, 0x53, 0x86, 0x03, 0x32, 0x26, 0x6B, 0xD8, 0xD8, 0xA7,
    0xDC, 0xE7, 0xF3, 0x42, 0x8D, 0x7E, 0xE8, 0x98, 0x30, 0xFF, 0x6F, 0xB2,
    0xBD, 0x03, 0x35, 0x71, 0xD3, 0xF6, 0x08, 0x0F, 0xBE, 0x85, 0x0C, 0x45,
    0x9E, 0x70, 0xC6, 0x84, 0x15, 0xA3, 0xC4, 0x2E, 0x60, 0xAD, 0xC9, 0xB9,
    0x99, 0x95, 0x4A, 0xBD, 0xD9, 0x43, 0xA5, 0x56, 0x28, 0xE9, 0xCF, 0x96,
    0x8A, 0x05, 0x40, 0xCF, 0x34, 0x64, 0x71, 0x69, 0xCA, 0x92, 0x9F, 0x1F,
    0x01, 0x48, 0xDB, 0xFD, 0x73, 0x29, 0x41, 0x94, 0x9A, 0x92, 0xEF, 0x74,
    0x71, 0xA5, 0xE0, 0xF9, 0x7D, 0x11, 0xCF, 0x6B, 0x5A, 0x81, 0x74, 0x77,
    0x1D, 0x25, 0xC1, 0x18, 0x9B, 0xDE, 0xF3, 0x88, 0x18, 0x88, 0x2B, 0x3A,
    0xD7, 0xD4, 0xDA, 0x57, 0xF6, 0xE0, 0xE1, 0xCC, 0xD2, 0x83, 0x61, 0xEA,
    0xDF, 0xBE, 0xC6, 0xEE, 0x82, 0x29, 0x31, 0x03, 0xC3, 0xB7, 0x44, 0xD2,
    0x8A, 0x00, 0x1E, 0x4F, 0x40, 0x74, 0x56, 0x72, 0xCA, 0x4F, 0xAD, 0x44,
    0x6C, 0x20, 0xD7, 0xA3, 0xA2, 0xF4, 0xDA, 0x32, 0x51, 0xCE, 0xAA, 0x8D,
    0x7F, 0xD1, 0xD1, 0xFB, 0xC0, 0xA3, 0x3E, 0xB9, 0xB4, 0x8E, 0x54, 0xDA,
    0xE9, 0x7A, 0xD6, 0xBA, 0xFD, 0x54, 0xB7, 0x50, 0x2D, 0x02, 0xA4, 0xC9,
    0x8A, 0x1E, 0x82, 0xB5, 0x11, 0x8E, 0x38, 0xAA, 0xDE, 0x2F, 0x84, 0x64,
    0x44, 0x03, 0xA6, 0xF9, 0x4C, 0xD7, 0x6A, 0x8F, 0x25, 0x6F, 0xD6, 0x7B,
    0xF8, 0x47, 0x71, 0x33, 0x28, 0x2A, 0x25, 0x01, 0x1D, 0xEA, 0x8C, 0xBA,
    0x62, 0x65, 0xE0, 0x5B, 0x5E, 0xA8, 0x6F, 0x5F, 0xEE, 0xA9, 0x09, 0x6B,
    0xB2, 0x35, 0xBB, 0x07, 0x40, 0x8A, 0x99, 0xB9, 0xBB, 0xDE, 0xEB, 0x7B,
    0x92, 0x73, 0xC5, 0x7A, 0x5E, 0x71, 0xA9, 0x5A, 0x3A, 0xC1, 0x76, 0x74,
    0x4F, 0x0C, 0x37, 0x9A, 0x9C, 0x66, 0xDE, 0x41, 0x40, 0xB3, 0xBF, 0x58,
    0x25, 0x88, 0x05, 0x0B, 0x3B, 0x62, 0x06, 0x89, 0x12, 0x5E, 0x2C, 0x82,
    0x7E, 0x18, 0x93, 0x60, 0xAE, 0x71, 0x6A, 0x79, 0x1C, 0x5F, 0xB6, 0x70,
    0x7C, 0xEF, 0x16, 0x3E, 0x6C, 0x9F, 0x88, 0x0F, 0x6C, 0x39, 0xE9, 0xED,
    0xCB, 0x01, 0x3C, 0x27, 0x08, 0x78, 0xFA, 0x60, 0xB5, 0xD4, 0x14, 0xAB,
    0x10, 0xB0, 0xE4, 0xA2, 0x8F, 0x80, 0x4F, 0x6E, 0xB7, 0x45, 0xCB, 0x81,
    0x84, 0x86, 0x83, 0xFE, 0x15, 0x8B, 0x21, 0x59, 0xD7, 0x83, 0x2A, 0x4F,
    0x68, 0x5B, 0x61, 0xC1, 0x49, 0x55, 0xA4, 0xB3, 0x03, 0xD8, 0x2C, 0x7F,
    0x2F, 0xDD, 0xED, 0x8E, 0xD0, 0xF2, 0xBD, 0x12, 0x4D, 0x1E, 0x53, 0x4B,
    0x1D, 0x5F, 0x08, 0x25, 0x9B, 0x3D, 0xA2, 0xA1, 0xEA, 0x44, 0x99, 0x6D,
    0x71, 0x3B, 0x48, 0x49, 0xFD, 0x2E, 0x5A, 0x71, 0xEC, 0xA0, 0x86, 0x13,
    0x26, 0x02, 0x0B, 0x49, 0xF8, 0x10, 0xD6, 0x60, 0xAA, 0x6B, 0x02, 0xDF,
    0xE5, 0xAA, 0xBA, 0x26, 0xFA, 0xCD, 0x50, 0xDE, 0xE3, 0x31, 0x93, 0x53,
    0x4E, 0x0F, 0x65, 0x77, 0xF9, 0xBF, 0x29, 0x95, 0x86, 0xB9, 0x7E, 0x83,
    0x45, 0xDB, 0xF2, 0x4A, 0x05, 0x3C, 0x2D, 0xDD, 0x58, 0x6A, 0xEA, 0xFE,
    0x53, 0x77, 0x7F, 0x2F, 0x05, 0xF7, 0x29, 0x3C, 0x17, 0x69, 0x36, 0xB8,
    0xDB, 0x33, 0x13, 0x8D, 0x76, 0xFD, 0xA5, 0xBC, 0x65, 0xAE, 0xFD, 0xC7,
    0xED, 0x48, 0xC9, 0x4C, 0x30, 0x40, 0xD6, 0x38, 0x5A, 0xCE, 0x1A, 0x04,
    0x8F, 0xF8, 0xEE, 0x44, 0x60, 0xAD, 0x3A, 0x94, 0xA3, 0x8C, 0x26, 0x63,
    0x5B, 0xC1, 0x18, 0xC6, 0x73, 0x7F, 0x12, 0x20, 0x04, 0xB5, 0x67, 0x5A,
    0x29, 0xB5, 0xE9, 0x8D, 0x28, 0x92, 0x05, 0xDA, 0x67, 0xA4, 0x9F, 0xB8,
    0xCC, 0x35, 0x0F, 0x2B, 0xF1, 0xB9, 0x50, 0x46, 0xEF, 0xA0, 0x7D, 0xC6,
    0x42, 0xEA, 0x38, 0x4E, 0xE8, 0xB4, 0xE2, 0xB1, 0x4C, 0x65, 0x18, 0xD7,
    0x18, 0x73, 0xD9, 0x6D, 0x28, 0x24, 0x6D, 0xEB, 0x4D, 0xF7, 0xBD, 0x31,
    0xAC, 0x8C, 0x80, 0xC6, 0xF8, 0x72, 0x5B, 0xAE, 0xF0, 0x16, 0x88, 0x25,
    0x9A, 0xEF, 0xB2, 0xE5, 0x8E, 0xAD, 0xD0, 0xC2, 0x8D, 0x96, 0x80, 0xB6,
    0x00, 0xD0, 0x08, 0x2C, 0x8C, 0xE1, 0x0D, 0xC3, 0x57, 0xC0, 0x2B, 0xF7,
    0xA5, 0x5A, 0x82, 0xFF, 0x69, 0xAD, 0x01, 0xE6, 0x42, 0xCD, 0x38, 0x4D,
    0x4E, 0xB6, 0x6D, 0xC6, 0xA4, 0x6A, 0x53, 0x5C, 0x13, 0x92, 0x05, 0x36,
    0x02, 0xB3, 0x98, 0x8E, 0x9E, 0xBB, 0x48, 0x74, 0xE3, 0x2E, 0x86, 0x84,
    0xFF, 0x80, 0xB4, 0xEE, 0x94, 0x3F, 0xBB, 0x37, 0xD7, 0xC3, 0xA8, 0x4B,
    0x5A, 0x85, 0x5A, 0x6A, 0xB7, 0x4D, 0x4B, 0xB3, 0x47, 0x6F, 0x6C, 0x43,
    0xBA, 0x12, 0x38, 0x61, 0xA1, 0x61, 0x2C, 0x7B, 0x50, 0xFA, 0x3A, 0x05,
    0x4C, 0x16, 0x0E, 0x2F, 0x88, 0x6C, 0xCF, 0xAC, 0x4F, 0xB7, 0xD9, 0x0E,
    0xC7, 0x55, 0x72, 0x9D, 0xF5, 0x64, 0xC4, 0xB8, 0x9A, 0x82, 0x18, 0xDA,
    0x51, 0x2C, 0x7F, 0x39, 0xC1, 0x17, 0xB9, 0xF4, 0x2F, 0x98, 0x3B, 0x55,
    0x7B, 0x69, 0xCC, 0x83, 0xE3, 0xDE, 0x75, 0x44, 0xFD, 0x55, 0x06, 0x73,
    0x47, 0xC7, 0xEE, 0x47, 0xA2, 0x3B, 0x35, 0x17, 0xF0, 0xF3, 0x0B, 0xE8,
    0x01, 0x97, 0x77, 0x67, 0x28, 0x14, 0xA4, 0x3F, 0x96, 0x74, 0xA2, 0xA4,
    0x35, 0xE3, 0x1F, 0x69, 0x41, 0x5A, 0x89, 0x4F, 0x19, 0x06, 0xDF, 0xD7,
    0x57, 0xC0, 0x54, 0x27, 0x72, 0x0F, 0x36, 0xE0, 0xF9, 0x57, 0xC0, 0x9A,
    0x56, 0xFD, 0x5C, 0x19, 0xC5, 0x79, 0x50, 0x88, 0xB8, 0x70, 0x57, 0xDB,
    0x55, 0x98, 0xA8, 0x2F, 0xBB, 0xB8, 0xAF, 0xF8, 0xE5, 0x33, 0x0D, 0x40,
    0xBE, 0xBC, 0x3C, 0x7E, 0xC8, 0xFF, 0x11, 0x7C, 0x12, 0x39, 0x76, 0x12,
    0x9C, 0x8D, 0x9C, 0x95, 0x23, 0x6A, 0x1A, 0xE9, 0xAF, 0xD7, 0x9B, 0xD1,
    0x8E, 0xA1, 0x32, 0x2F, 0x17, 0x4D, 0x84, 0x9C, 0xCC, 0x8F, 0xA9, 0x14,
    0xEC, 0x86, 0xCF, 0x1A, 0xFD, 0x1D, 0x45, 0x14, 0x5D, 0x9B, 0x0C, 0x63,
    0x3A, 0xF2, 0x84, 0x44, 0xAA, 0x4D, 0x69, 0x76, 0x15, 0x32, 0xD2, 0xCB,
    0x6B, 0x03, 0x30, 0x9A, 0x6A, 0x64, 0x66, 0x8C, 0x06, 0xEC, 0x70, 0xCC,
    0x33, 0x92, 0xAA, 0x43, 0x09, 0x06, 0xE2, 0x52, 0x48, 0xE1, 0x76, 0x09,
    0x0F, 0x44, 0x7B, 0x40, 0x04, 0x7B, 0xD7, 0xBD, 0x8C, 0xEC, 0xBD, 0x8E,
    0x16, 0xBF, 0x2F, 0x40, 0x48, 0x72, 0x78, 0xA0, 0x45, 0x16, 0x90, 0x37,
    0xAA, 0x7A, 0xCA, 0x8F, 0x7A, 0x6C, 0xAA, 0x68, 0xB3, 0xEB, 0x63, 0xE9,
    0x7F, 0xD0, 0xE7, 0x3A, 0x82, 0xFF, 0xEB, 0x45, 0x7F, 0x75, 0xDC, 0xB5,
    0xF9, 0x6A, 0x27, 0x8E, 0x49, 0x39, 0x0A, 0xAE, 0x98, 0x24, 0xC4, 0xEE,
    0xF5, 0x80, 0xBC, 0xF5, 0x30, 0x4F, 0x7D, 0x5A, 0x0C, 0x79, 0x0F, 0x37,
    0x3D, 0x9C, 0x53, 0xD6, 0xEE, 0x95, 0xEA, 0x85, 0x54, 0xE6, 0x9A, 0x2A,
    0xCF, 0x80, 0xDC, 0x14, 0x76, 0x8D, 0x9C, 0x83, 0x1F, 0x40, 0xAC, 0x66,
    0x27, 0x67, 0x69, 0xB6, 0xBC, 0x05, 0x7E, 0xE9, 0xC6, 0xCE, 0xB2, 0xD6,
    0xB1, 0xAC, 0x79, 0x20, 0x13, 0x24, 0xCE, 0xC4, 0xD1, 0x02, 0x6B, 0x88,
    0xD4, 0x0D, 0xD9, 0x41, 0x9D, 0x94, 0x71, 0xF2, 0x06, 0x15, 0xDF, 0x34,
    0xF1, 0x4D, 0xB3, 0x02, 0x84, 0xAE, 0x83, 0x7D, 0xDC, 0xC8, 0xC3, 0x3E,
    0x53, 0x01, 0x5C, 0x1D, 0xB0, 0xFE, 0x84, 0xA3, 0x73, 0xA9, 0x6A, 0x8A,
    0x13, 0xD3, 0xEF, 0x8B, 0xEA, 0x90, 0x78, 0x87, 0xB5, 0xFF, 0xCE, 0x34,
    0xF9, 0x78, 0x7B, 0xAD, 0xB8, 0x62, 0xDE, 0xB5, 0xB5, 0x6C, 0xF9, 0xB3,
    0x67, 0x2E, 0x83, 0x9D, 0x52, 0xBE, 0x44, 0xD1, 0x89, 0xD6, 0x2B, 0xE4,
    0x58, 0x67, 0x9D, 0xAC, 0xC4, 0x25, 0xEB, 0x50, 0x20, 0x1F, 0x2E, 0xF5,
    0xE7, 0x38, 0x05, 0xEF, 0x70, 0x9C, 0xF3, 0x0C, 0x4A, 0xB4, 0x47, 0xD4,
    0x1C, 0x89, 0x9C, 0xD3, 0x09, 0x6A, 0x97, 0xCC, 0xF3, 0xC6, 0x85, 0x4A,
    0xB3, 0x0F, 0xAF, 0xF0, 0x0A, 0xC0, 0x40, 0xE0, 0x22, 0x19, 0xEB, 0x81,
    0x63, 0x3E, 0x5D, 0x36, 0x28, 0x63, 0xCD, 0x48, 0xBB, 0xB5, 0x3F, 0x52,
    0xD2, 0xAB, 0x27, 0x01, 0xC4, 0x75, 0x67, 0xF5, 0xC2, 0x89, 0x43, 0x48,
    0xCF, 0x4B, 0x8C, 0xB2, 0xED, 0x11, 0x87, 0x5F, 0x16, 0xFF, 0xC7, 0xDC,
    0x77, 0x4A, 0xFE, 0xEA, 0xEE, 0xC4, 0x9D, 0x9F, 0xB4, 0xA2, 0x3A, 0x01,
    0xD3, 0x20, 0xEB, 0x95, 0x84, 0xAD, 0x4C, 0xCC, 0xD9, 0x12, 0xA1, 0x4B,
    0xC7, 0xC3, 0x60, 0x26, 0xBA, 0xDB, 0x26, 0x9F, 0xF1, 0x3A, 0x66, 0xE1,
    0x8C, 0x06, 0xD1, 0x47, 0x0B, 0x77, 0x84, 0xAA, 0xEE, 0x8A, 0x3F, 0xCB,
    0x51, 0x8E, 0x15, 0x71, 0xB7, 0x45, 0x79, 0x8C, 0x34, 0xC3, 0xB2, 0x56,
    0xE5, 0xC4, 0x24, 0xAF, 0x05, 0x3F, 0x56, 0x86, 0x87, 0xAE, 0xA2, 0x96,
    0xA4, 0x78, 0xB2, 0x29, 0x34, 0xA5, 0xE3, 0x02, 0x52, 0x5A, 0xD6, 0x5F,
    0x8F, 0xE7, 0x55, 0xE8, 0x89, 0xCA, 0x1F, 0xDD, 0x29, 0x81, 0x80, 0xBA,
    0xA0, 0x0C, 0x28, 0xFA, 0x85, 0x8A, 0xAC, 0xA3, 0x8F, 0x48, 0xB5, 0xC2,
    0xB8, 0xD1, 0x87, 0x0F, 0x89, 0x5B, 0xA1, 0x94, 0x2C, 0xE4, 0x05, 0xC6,
    0x60, 0x94, 0x57, 0xC2, 0x69, 0x53, 0x01, 0xA9, 0xEF, 0xC4, 0x3D, 0x92,
    0x21, 0x0D, 0x78, 0x2C, 0xFE, 0x58, 0x65, 0xBB, 0xB7, 0x0A, 0x39, 0x6A,
    0x20, 0xC4, 0xAE, 0x4B, 0xF0, 0x57, 0xEE, 0x0A, 0x59, 0xA0, 0x71, 0xD1,
    0xBF, 0xB9, 0x39, 0xC7, 0x12, 0x3C, 0xAA, 0xCA, 0xFB, 0xCE, 0x0F, 0x43,
    0x48, 0xDD, 0xA7, 0xB5, 0x0D, 0x25, 0xF3, 0x88, 0xA7, 0xD4, 0xDA, 0xDC,
    0x6B, 0xBE, 0x18, 0x36, 0x09, 0x82, 0xE8, 0x99, 0x74, 0xD1, 0x83, 0xFF,
    0x94, 0x2B, 0xA5, 0xD9, 0xC0, 0x8E, 0xBF, 0x0C, 0xF3, 0xAF, 0xCE, 0x06,
    0x4B, 0x86, 0x4D, 0x2E, 0xB8, 0xAA, 0x0C, 0xA5, 0x0C, 0xC3, 0x38, 0xA8,
    0x60, 0x0E, 0xD9, 0x58, 0x71, 0x10, 0x00, 0x37, 0x99, 0x81, 0x9D, 0xB8,
    0x4F, 0xEF, 0x74, 0xAC, 0x37, 0xBC, 0x57, 0x53, 0x4E, 0xAF, 0xAE, 0x87,
    0x78, 0xFB, 0x82, 0x18, 0x60, 0x78, 0x99, 0x98, 0x16, 0x01, 0x41, 0x42,
    0x5E, 0xDE, 0xDF, 0xA9, 0xBD, 0x7B, 0x93, 0xDC, 0xC4, 0xB8, 0x9F, 0x4E,
    0xD5, 0x8A, 0x20, 0x15, 0x49, 0x0C, 0xCF, 0x0A, 0x2E, 0x6E, 0xDE, 0xCB,
    0x67, 0x59, 0x1B, 0x1B, 0xB8, 0x24, 0xB0, 0xB0, 0xE4, 0x13, 0x1A, 0x8E,
    0x4E, 0x39, 0xF4, 0x52, 0x69, 0xE3, 0xDC, 0x95, 0xC0, 0xC8, 0xC0, 0xB3,
    0x96, 0xE2, 0x72, 0x99, 0xA1, 0xE9, 0xFD, 0xC4, 0x9E, 0xFA, 0x43, 0xD3,
    0x62, 0x17, 0x6B, 0x54, 0x35, 0x7D, 0xB1, 0xBF, 0xA9, 0xB3, 0x03, 0x18,
    0x36, 0x73, 0xE2, 0xC7, 0xC2, 0xD9, 0xB2, 0x35, 0x6B, 0x7D, 0x9F, 0x55,
    0x38, 0xF9, 0xA1, 0x61, 0xA0, 0x99, 0x34, 0x02, 0x39, 0x29, 0x21, 0x97,
    0xDF, 0x87, 0xDA, 0xCB, 0x87, 0x75, 0x4F, 0xFA, 0x57, 0x54, 0x69, 0x3A,
    0x97, 0x24, 0xFE, 0xBE, 0x2F, 0xB4, 0x7B, 0x12, 0xC3, 0xA7, 0x0A, 0xE9,
    0x39, 0x9A, 0x2C, 0x08, 0x6A, 0x05, 0x66, 0x0F, 0xD3, 0x38, 0xA5, 0xD9,
    0xC8, 0x83, 0x2A, 0x6B, 0xC5, 0x44, 0xE3, 0x7B, 0x01, 0xAB, 0x32, 0x96,
    0x8C, 0x23, 0x2A, 0x14, 0x7C, 0x3C, 0xBB, 0x46, 0x51, 0x04, 0x12, 0xB6,
    0x1B, 0xB6, 0xE3, 0xF0, 0xAD, 0x23, 0xEB, 0x67, 0xF8, 0xB9, 0x95, 0xC1,
    0x98, 0x55, 0xE3, 0x75, 0xA7, 0x1A, 0x7C, 0x3A, 0xB8, 0x9D, 0xA7, 0x12,
    0xDF, 0xFC, 0xA0, 0x1A, 0xBB, 0x5C, 0xC8, 0x69, 0x95, 0x5A, 0x67, 0xE3,
    0x5A, 0xFC, 0x14, 0x3F, 0x17, 0x6E, 0x54, 0x0A, 0x80, 0xA9, 0x51, 0xE8,
    0x41, 0x20, 0xFD, 0x58, 0x12, 0x31, 0x45, 0xD3, 0x50, 0xF4, 0x46, 0x67,
    0x75, 0x4B, 0x21, 0x3C, 0x57, 0x6D, 0xDD, 0x7C, 0xAC, 0xE0, 0xA7, 0x86,
    0xC9, 0xF8, 0xA7, 0x09, 0xB5, 0x45, 0x1C, 0xE9, 0xD5, 0xB5, 0xC0, 0xC5,
    0x67, 0x2F, 0xE9, 0x34, 0xA1, 0x0D, 0xCD, 0xB4, 0xE5, 0x3D, 0xB2, 0x41,
    0x9C, 0x72, 0x4C, 0x88, 0x22, 0x0F, 0x48, 0xE6, 0x78, 0x3C, 0xCD, 0xEF,
    0x06, 0x70, 0x10, 0xBF, 0x66, 0x14, 0xE0, 0xA9, 0x2F, 0xBA, 0xF5, 0x96,
    0x47, 0xB0, 0x79, 0x59, 0x98, 0x0A, 0x9B, 0x16, 0xD3, 0xB1, 0x11, 0xB1,
    0xBD, 0xDA, 0x81, 0xDB, 0x4F, 0x56, 0xBC, 0x68, 0x15, 0xA8, 0xBA, 0x10,
    0x0B, 0xD1, 0xBE, 0x76, 0x2C, 0x4D, 0xA8, 0x4F, 0x3F, 0xC8, 0x9E, 0xA5,
    0xAE, 0x88, 0x6B, 0x7D, 0x11, 0xFE, 0x89, 0xFC, 0x17, 0x0F, 0x0C, 0x32,
    0x51, 0xF4, 0xDB, 0x3E, 0x94, 0x55, 0xC6, 0xDA, 0xED, 0xD5, 0x0F, 0x87,
    0xB9, 0x7F, 0x33, 0xC3, 0xD1, 0x0B, 0x83, 0x63, 0x32, 0x72, 0x36, 0xED,
    0xA1, 0x2A, 0xF8, 0x6E, 0x88, 0x42, 0xC4, 0x94, 0xBB, 0x00, 0x64, 0xC1,
    0x72, 0x25, 0x36, 0x53, 0xBB, 0x71, 0x02, 0x68, 0x78, 0xC1, 0x7A, 0x5F,
    0x70, 0xD0, 0x8D, 0x15, 0x94, 0xC0, 0x90, 0xA7, 0x81, 0x4F, 0x87, 0x30,
    0x49, 0x44, 0xC8, 0x96, 0xED, 0x9C, 0x6E, 0xD4, 0x9D, 0x2A, 0x81, 0x73,
    0xD3, 0x89, 0x8C, 0x32, 0x3C, 0xAD, 0xAC, 0x0B, 0x99, 0x7A, 0xD8, 0x25,
    0x94, 0x77, 0xB0, 0x21, 0x24, 0x4E, 0xE6, 0x47, 0x6C, 0xB1, 0x6B, 0x87,
    0xF9, 0x76, 0x95, 0x65, 0x2D, 0x05, 0x03, 0x55, 0x24, 0x1F, 0x6B, 0xAF,
    0xC2, 0x6B, 0x17, 0xAC, 0x5F, 0x0C, 0x74, 0x06, 0xAB, 0xDD, 0x04, 0x30,
    0xC8, 0xD6, 0x4F, 0xD1, 0xE8, 0xA2, 0xF4, 0xB1, 0xE0, 0x47, 0x19, 0x00,
    0x13, 0x83, 0x87, 0xEB, 0xC7, 0xD9, 0xAB, 0x6C, 0x57, 0xDA, 0x00, 0xD4,
    0x9D, 0x4D, 0x6D, 0x66, 0xC7, 0x51, 0xB5, 0xCF, 0x1F, 0x91, 0x04, 0x16,
    0x30, 0xCC, 0xCE, 0x7B, 0xD2, 0x69, 0x77, 0xED, 0xCC, 0x07, 0x6D, 0xEB,
    0x63, 0x2B, 0x99, 0x16, 0x14, 0x1E, 0x0D, 0x1D, 0xDA, 0x2C, 0x98, 0x16,
    0xDF, 0xDC, 0xD4, 0x92, 0xD3, 0xCC, 0x6A, 0x35, 0x80, 0xF1, 0xCC, 0x0F,
    0xF1, 0xF7, 0x43, 0xE6, 0x82, 0xC6, 0xF5, 0x6A, 0x2D, 0x16, 0xCC, 0xFB,
    0x28, 0x23, 0xE0, 0x27, 0xB5, 0xC1, 0x83, 0x88, 0x17, 0xB2, 0x9E, 0x24,
    0xBD, 0xA6, 0x17, 0x3B, 0xBD, 0xF8, 0xFB, 0x96, 0xC4, 0x77, 0x93, 0x2E,
    0x51, 0x45, 0xB1, 0x45, 0x97, 0x3A, 0x08, 0x78, 0xA0, 0x34, 0x6B, 0x4B,
    0x5C, 0xDF, 0xFE, 0x40, 0x04, 0x8A, 0xE9, 0xFB, 0xCC, 0x6C, 0xD6, 0x56,
    0x28, 0x89, 0x7F, 0xDE, 0x2A, 0xC3, 0xD9, 0x92, 0x8F, 0x22, 0x03, 0x1B,
    0xEA, 0x22, 0xAB, 0x99, 0x18, 0x5B, 0x47, 0xC3, 0x06, 0x02, 0xBC, 0xBB,
    0x42, 0xB4, 0x2C, 0x05, 0x61, 0x97, 0x86, 0x9B, 0x77, 0x91, 0x5B, 0x48,
    0xAC, 0x68, 0x23, 0x01, 0x62, 0x7F, 0xA5, 0x7F, 0xF0, 0x7B, 0x6F, 0x04,
    0x37, 0x91, 0x13, 0xF4, 0xEB, 0x3C, 0x2E, 0x2C, 0x9A, 0xE6, 0x1F, 0xB1,
    0x5F, 0xAF, 0xD4, 0x8A, 0xDB, 0x77, 0xCC, 0x0A, 0xA7, 0x7C, 0xC4, 0x46,
    0x02, 0xA5, 0x8F, 0x81, 0xEF, 0x92, 0x39, 0xB0, 0x8C, 0xEA, 0xFA, 0x4C,
    0x77, 0x30, 0x21, 0xE8, 0x6D, 0xA0, 0x4C, 0x0A, 0x65, 0xFA, 0x20, 0xD1,
    0x7F, 0xCE, 0x69, 0x11, 0x1F, 0x63, 0xFB, 0x70, 0xCB, 0x42, 0xE1, 0xB5,
    0x09, 0xB2, 0x02, 0x83, 0x67, 0xE8, 0x60, 0x20, 0x34, 0x7A, 0x59, 0xC0,
    0x7A, 0x7A, 0xC1, 0x8D, 0x83, 0x0E, 0x90, 0x3A, 0x72, 0xAF, 0x6C, 0x9F,
    0xA5, 0x95, 0x1B, 0x1D, 0x2D, 0x98, 0x52, 0xDF, 0xCF, 0x55, 0x2A, 0xAD,
    0x13, 0xD2, 0x38, 0xA5, 0x66, 0xBC, 0x69, 0xA0, 0xA3, 0x37, 0x7D, 0x0D,
    0x70, 0x63, 0x17, 0x49, 0x43, 0xA1, 0x0D, 0xDA, 0xAE, 0xD1, 0x02, 0xFC,
    0x0F, 0x6E, 0x7F, 0x26, 0xD8, 0x4C, 0x16, 0xAB, 0xB8, 0x6A, 0x64, 0x03,
    0x69, 0xF4, 0x32, 0x69, 0xCD, 0x17, 0x85, 0xF9, 0xCE, 0xD2, 0xBE, 0xAE,
    0x9C, 0x59, 0xD8, 0x50, 0xF1, 0x93, 0x1C, 0x06, 0xC6, 0x32, 0x61, 0xC2,
    0x54, 0x85, 0x88, 0xF3, 0x77, 0xFD, 0xA3, 0x8D, 0x82, 0x94, 0x63, 0xB4,
    0xCA, 0x4C, 0x47, 0xE5, 0xB1, 0x02, 0xA4, 0x53, 0xBB, 0xDF, 0x43, 0xE7,
    0x72, 0xD7, 0xE1, 0xD0, 0x18, 0x06, 0xCE, 0x63, 0x63, 0x8A, 0x96, 0xAF,
    0x1F, 0xEA, 0xCD, 0x9B, 0x74, 0x21, 0xEA, 0x52, 0x7A, 0xDE, 0xBA, 0x98,
    0xA7, 0xAE, 0x01, 0x9A, 0xD5, 0x94, 0xF7, 0x07, 0x6F, 0x46, 0x49, 0xE8,
    0x08, 0xEB, 0xA7, 0x4A, 0x6D, 0xF0, 0xC6, 0xFD, 0x9E, 0x64, 0xFB, 0xDB,
    0x47, 0x0A, 0x22, 0xB5, 0x93, 0xF3, 0x5C, 0xA4, 0xF7, 0xEE, 0x5E, 0x8C,
    0xBD, 0x15, 0x06, 0x65, 0xDA, 0xBE, 0xF5, 0xA1, 0x70, 0x18, 0x1B, 0x36,
    0x91, 0x34, 0x65, 0x7B, 0xE2, 0x20, 0xA9, 0xA2, 0x4B, 0x9E, 0xBC, 0x7F,
    0xAE, 0x5C, 0xCE, 0x58, 0xC6, 0x5F, 0x0B, 0x30, 0x73, 0xB6, 0xD4, 0x92,
    0xD9, 0x85, 0xFE, 0x64, 0x53, 0xD4, 0x03, 0xAF, 0x6B, 0x5E, 0x46, 0x69,
    0x5A, 0xEC, 0x65, 0x73, 0xCD, 0x98, 0xE1, 0x11, 0x9C, 0x24, 0xE1, 0xCD,
    0x81, 0x7D, 0x91, 0xF9, 0xD3, 0x86, 0x9E, 0xA7, 0x38, 0x00, 0xA0, 0x98,
    0x21, 0xD7, 0x4A, 0x1D, 0x86, 0xF4, 0x4E, 0x96, 0x34, 0x8E, 0x9F, 0x25,
    0x3D, 0x95, 0xA3, 0xD8, 0xB7, 0x7A, 0xFD, 0xB0, 0xA5, 0xC8, 0x31, 0x73,
    0xA1, 0x91, 0x33, 0x34, 0x88, 0xAA, 0xFB, 0xB8, 0x56, 0x2A, 0xE3, 0xD6,
    0xA7, 0x9C, 0x98, 0x4B, 0x4B, 0xDC, 0x6D, 0xC5, 0x29, 0xA4, 0xC0, 0xC8,
    0x11, 0xCD, 0x1E, 0x19, 0x6B, 0x9E, 0x82, 0x09, 0x90, 0x48, 0xAF, 0xDE,
    0x08, 0xBD, 0xBD, 0xC0, 0x5F, 0x9B, 0xB2, 0x22, 0x1A, 0xBD, 0xD7, 0x0F,
    0x9D, 0x1C, 0x34, 0xFA, 0xA2, 0xFC, 0x96, 0xE0, 0x47, 0xA7, 0x1A, 0xC1,
    0x29, 0xBA, 0x3D, 0xCC, 0x3B, 0x5F, 0x84, 0xCF, 0x24, 0x27, 0x1C, 0x39,
    0x3D, 0xB3, 0x99, 0xD1, 0x06, 0x31, 0x7B, 0x55, 0xB8, 0x1B, 0x46, 0x35,
    0x9B, 0x77, 0x21, 0xEC, 0x8F, 0x31, 0x9C, 0x8F, 0xA8, 0x97, 0xE9, 0x08,
    0xC3, 0x86, 0xB4, 0xF0, 0x98, 0x2C, 0xF1, 0xEF, 0x96, 0x52, 0x3A, 0xC5,
    0x9C, 0x91, 0x75, 0xD4, 0x5F, 0xC1, 0x78, 0x79, 0x1A, 0x7A, 0xC8, 0x9A,
    0x10, 0x40, 0x7A, 0xFE, 0xAF, 0x28, 0x63, 0x5E, 0xF6, 0x10, 0x42, 0xC6,
    0xC5, 0x2D, 0x79, 0x8C, 0xB2, 0x54, 0x2E, 0xFE, 0x25, 0x7F, 0x12, 0x69,
    0x47, 0xD9, 0x71, 0x97, 0xF5, 0xA9, 0xC8, 0x0C, 0x9A, 0x28, 0x1F, 0xDE,
    0xFB, 0xD9, 0xDC, 0x79, 0xD8, 0x6E, 0x70, 0xFD, 0x74, 0x20, 0xD5, 0xC9,
    0x51, 0xCF, 0xC3, 0x2D, 0x57, 0xA9, 0xEE, 0x65, 0xAA, 0xC3, 0x17, 0xD6,
    0x21, 0xBA, 0xFA, 0x43, 0x53, 0xD2, 0x84, 0x86, 0x0C, 0xD5, 0x51, 0x48,
    0x64, 0x40, 0x76, 0x47, 0x1D, 0xD5, 0xEF, 0x24, 0x71, 0x85, 0xFC, 0xF7,
    0xCB, 0xCA, 0xE5, 0x3E, 0x56, 0x39, 0x22, 0x50, 0x4F, 0xAF, 0x5D, 0x3A,
    0xE2, 0xE4, 0x09, 0x3B, 0x68, 0xCC, 0xFE, 0xD5, 0x0C, 0x9D, 0xBB, 0xD2,
    0x16, 0xA1, 0xA5, 0x82, 0x8D, 0x7B, 0xAC, 0xEF, 0x7A, 0xE2, 0x88, 0xB6,
    0x19, 0x8F, 0x24, 0x1E, 0x53, 0x7A, 0x8C, 0xE9, 0xD3, 0x62, 0xC8, 0x9E,
    0xD1, 0x35, 0x60, 0x8E, 0xC3, 0x23, 0xAB, 0x70, 0x83, 0x38, 0xFC, 0xF4,
    0x6F, 0xFF, 0x2E, 0x61, 0xDF, 0x07, 0xA8, 0xA4, 0xBE, 0xD3, 0xD7, 0x97,
    0x3E, 0x3D, 0xC9, 0xAD, 0xE6, 0xCB, 0x47, 0x09, 0x34, 0xC7, 0x1F, 0xD9,
    0x7C, 0xBC, 0xE5, 0xB0, 0xE2, 0x4D, 0x9E, 0x2A, 0xFA, 0xFF, 0x40, 0x30,
    0xE5, 0xFE, 0xFF, 0x55, 0x38, 0x26, 0xC5, 0x0E, 0x16, 0xBD, 0x53, 0xC5,
    0x24, 0xA7, 0xE0, 0x5A, 0x89, 0xAF, 0x3B, 0x07, 0xA2, 0xF7, 0xCD, 0x42,
    0x21, 0x8C, 0x93, 0x34, 0xCF, 0xF3, 0x65, 0x7A, 0x33, 0x1A, 0x8B, 0xE4,
    0x57, 0x54, 0x9D, 0x1E, 0xCC, 0x38, 0xBE, 0xAC, 0xF2, 0x59, 0x4E, 0x4A,
    0x05, 0x9C, 0x64, 0xDC, 0x6E, 0x87, 0x4A, 0x93, 0x94, 0xE6, 0x04, 0x64,
    0xBD, 0xEC, 0xA8, 0xC5, 0xCA, 0x8A, 0xBE, 0x34, 0xF4, 0x0B, 0xC1, 0x19,
    0x39, 0x59, 0xD7, 0xBD, 0x4B, 0xB2, 0xA3, 0xE7, 0xB7, 0x0E, 0x03, 0xDC,
    0x02, 0x03, 0x87, 0x5D, 0xC1, 0xB7, 0x8F, 0xD9, 0x1D, 0xFE, 0xA1, 0xDA,
    0x1D, 0x9C, 0x88, 0x60, 0x14, 0xBA, 0xCD, 0xA7, 0x38, 0xFB, 0x36, 0x45,
    0xE2, 0xAB, 0x47, 0x17, 0x41, 0x2C, 0x3E, 0x89, 0x2C, 0xA8, 0x79, 0xB2,
    0x91, 0x17, 0xEF, 0x63, 0x4D, 0xE8, 0x78, 0xB5, 0xEC, 0xFE, 0x32, 0xCA,
    0xCC, 0xE3, 0xD3, 0x7F, 0xD7, 0x93, 0x22, 0xD2, 0x6C, 0xAD, 0xD7, 0xC2,
    0xF6, 0x2E, 0x33, 0x7F, 0x4A, 0x7C, 0x99, 0xE5, 0xAB, 0x50, 0x58, 0x4D,
    0x4B, 0x38, 0x00, 0x51, 0xF0, 0x9A, 0x39, 0x7E, 0xB6, 0xFE, 0x86, 0xEC,
    0x61, 0x22, 0x30, 0x78, 0xE3, 0x86, 0xE0, 0xE7, 0xA4, 0x75, 0x5E, 0xF3,
    0x09, 0x5C, 0xC5, 0x5E, 0x57, 0xE0, 0x08, 0xDA, 0x3A, 0x50, 0x10, 0xE3,
    0xEF, 0x63, 0xCF, 0x14, 0x45, 0xFF, 0x94, 0xD4, 0x08, 0x0E, 0xD2, 0xA7,
    0xE3, 0x49, 0x5B, 0x99, 0xFC, 0x5C, 0x5B, 0x5F, 0x5F, 0xC1, 0x84, 0x2C,
    0x4E, 0x42, 0xF5, 0x62, 0x3F, 0xEC, 0x01, 0x6C, 0x65, 0x38, 0x1A, 0xD5,
    0xD4, 0x58, 0xA4, 0xC5, 0xEA, 0xF2, 0xB8, 0xF3, 0xBE, 0xD6, 0xA2, 0x27,
    0x39, 0x78, 0x7B, 0xB3, 0xF6, 0x49, 0x28, 0x3C, 0x0C, 0xB4, 0x1A, 0x10,
    0x14, 0x78, 0xD4, 0xB1, 0x71, 0x5E, 0xFF, 0x6D, 0x12, 0x88, 0xEA, 0xFE,
    0xDE, 0xF4, 0xD4, 0xD2, 0xC2, 0x51, 0x23, 0xD2, 0x33, 0x5B, 0xC4, 0x35,
    0x8A, 0xA1, 0x6E, 0x39, 0x8C, 0x31, 0x16, 0x76, 0x4A, 0x1B, 0xCB, 0x91,
    0x44, 0xFD, 0x64, 0xBB, 0xA6, 0xCB, 0x0A, 0x56, 0xEB, 0x55, 0x8D, 0xEA,
    0x94, 0x91, 0xF7, 0x9D, 0xE6, 0x9F, 0x13, 0xC1, 0xB8, 0x1E, 0xBC, 0x1E,
    0x8E, 0x2B, 0x3B, 0x87, 0x9A, 0xD8, 0x32, 0xF8, 0xC4, 0xAD, 0x69, 0x08,
    0xF9, 0x10, 0xD3, 0x9C, 0x89, 0x03, 0xA8, 0x96, 0xFF, 0x2D, 0x85, 0x81,
    0xF4, 0x65, 0x2F, 0x72, 0x60, 0x63, 0x54, 0x5F, 0x97, 0xB6, 0x46, 0xB5,
    0xB7, 0x7C, 0xE0, 0x16, 0x61, 0x36, 0x2F, 0x41, 0x4B, 0x81, 0xFE, 0x24,
    0x7B, 0xAB, 0x09, 0x34, 0xCD, 0xA0, 0x40, 0x0E, 0xB1, 0xB1, 0x05, 0xA2,
    0x88, 0x14, 0x22, 0xE3, 0xCC, 0x45, 0x62, 0xD2, 0x34, 0xC5, 0x9F, 0xEB,
    0x36, 0xF4, 0x26, 0x6C, 0xBF, 0x9E, 0xC1, 0x72, 0xF8, 0x6A, 0x23, 0xA7,
    0x87, 0xCF, 0xD2, 0x0A, 0x99, 0x37, 0xE5, 0x0D, 0x68, 0x47, 0xAC, 0x4D,
    0x52, 0x47, 0x00, 0x53, 0x15, 0x68, 0x43, 0x12, 0x9F, 0x9B, 0xF8, 0xD9,
    0xF9, 0x5F, 0x78, 0xAA, 0xB5, 0x41, 0xB9, 0x84, 0x0A, 0x3A, 0xC8, 0x2A,
    0x6F, 0xFF, 0x0B, 0xF0, 0xEE, 0xB4, 0xE8, 0xE4, 0x42, 0xCB, 0xBB, 0xDB,
    0x46, 0x31, 0xD0, 0x3A, 0xAA, 0x13, 0xB9, 0x9D, 0x98, 0x85, 0xE5, 0x8B,
    0x6C, 0xA3, 0xB6, 0xD1, 0x97, 0x6D, 0x81, 0x17, 0xF0, 0x63, 0x65, 0x81,
    0x40, 0x25, 0x31, 0xCC, 0xEF, 0x71, 0x56, 0x81, 0xA6, 0x81, 0xCA, 0x43,
    0xBB, 0xDC, 0x79, 0xAE, 0x61, 0x0E, 0x99, 0x14, 0xC2, 0x8F, 0xD8, 0x6E,
    0x54, 0xC0, 0x71, 0xCF, 0x98, 0x53, 0x0E, 0x0B, 0x64, 0x87, 0x6E, 0x88,
    0xF8, 0xA1, 0xC0, 0xB6, 0x30, 0x58, 0x35, 0xEC, 0x60, 0x04, 0x01, 0xC8,
    0x73, 0x24, 0xB4, 0xB0, 0x46, 0xAD, 0xC7, 0x4B, 0xFB, 0xEC, 0xC3, 0x0D,
    0x86, 0x44, 0x32, 0x07, 0xD1, 0xC9, 0x94, 0xA7, 0xE4, 0x4E, 0xD4, 0xD7,
    0x14, 0xFC, 0x9F, 0x7B, 0xD1, 0x3D, 0x94, 0x56, 0xC2, 0x7C, 0xED, 0x62,
    0xFD, 0x91, 0x87, 0x08, 0xA2, 0x6B, 0x44, 0xE5, 0x8C, 0x28, 0xAD, 0xF7,
    0x45, 0x15, 0xD3, 0x76, 0xAA, 0x29, 0x27, 0x0D, 0xC2, 0x72, 0x93, 0xF0,
    0x5A, 0xB8, 0xBE, 0xBB, 0xD4, 0x0E, 0xA7, 0x11, 0x13, 0xFC, 0xAA, 0x47,
    0xD2, 0x4D, 0x05, 0x93, 0x1B, 0xBD, 0x3A, 0x01, 0x0E, 0x53, 0x31, 0x33,
    0x3C, 0x23, 0x83, 0x8A, 0x65, 0x1E, 0x82, 0x66, 0x3C, 0x0F, 0x65, 0x67,
    0x20, 0xAE, 0x3A, 0x5A, 0xF8, 0x4D, 0x8F, 0xA9, 0x7C, 0xA0, 0x26, 0xBD,
    0x1F, 0xE3, 0x38, 0x4F, 0xBE, 0x3D, 0xCF, 0x9F, 0x50, 0x42, 0x95, 0x1C,
    0x76, 0x1A, 0x9A, 0x41, 0x08, 0xD9, 0xBA, 0xB9, 0xD6, 0xF6, 0x65, 0x41,
    0x22, 0xD4, 0xDF, 0xBD, 0x4E, 0xFD, 0x58, 0xD5, 0x1B, 0x07, 0x14, 0x31,
    0xE5, 0xA8, 0xBF, 0x88, 0x22, 0xCF, 0x70, 0x5B, 0x72, 0x9D, 0xC2, 0x6D,
    0x87, 0x0F, 0x6F, 0x1B, 0x6C, 0xEF, 0xD7, 0xD1, 0xC0, 0xEE, 0x48, 0xB4,
    0xA2, 0xCA, 0xC9, 0x7B, 0xB8, 0xE8, 0xA2, 0x5D, 0x39, 0x35, 0x89, 0xFA,
    0xEB, 0x78, 0xCE, 0x51, 0xB1, 0xBA, 0xE6, 0xA4,
])

_KEY_KLQ = bytes.fromhex('E24548EC690E5CAC')
_KEY_KSW = bytes.fromhex('397B4D58C9397B4D58C9')
_KEY_GDATA = bytes.fromhex('A7BF73A09F03D311856F0080ADA96E9B')
_KEY_PANDA = bytes.fromhex('3DD822666516E3B8C5D61871E719E05A')
_KEY_BAIDU = bytes.fromhex('D9A7A3BF85FF4377AD06CFFD1F94E9CC')
_KEY_ZEMANA = b'A8147B3ABF8533AB27FA9551B1FAA385'
_KEY_AMITI = b'AA79e10d15l6o2t8'
_GUID_ASQUARED_LC = b'{A4A1BFF9-301A-40d3-86D3-D1F29E413B28}'
_PHRASE_MB_V1 = b'XBXM8362QIXD9+637HCB02/VN0JF6Z3)cB9UFZMdF3I.*c.,c5SbO7)WNZ8CY1(XMUDb'
_PHRASE_MB_V2 = b'Go9r%8hhAl7Ari;vnQ8wwkmeostfETkzLEf5*+6u8MF.CbsYKbTt9w.cVJbJ+pzyvrsT'

_KEY_MSDEF_PC = bytes([
    0x1E, 0x87, 0x78, 0x1B, 0x8D, 0xBA, 0xA8, 0x44, 0xCE, 0x69, 0x70, 0x2C,
    0x0C, 0x78, 0xB7, 0x86, 0xA3, 0xF6, 0x23, 0xB7, 0x38, 0xF5, 0xED, 0xF9,
    0xAF, 0x83, 0x53, 0x0F, 0xB3, 0xFC, 0x54, 0xFA, 0xA2, 0x1E, 0xB9, 0xCF,
    0x13, 0x31, 0xFD, 0x0F, 0x0D, 0xA9, 0x54, 0xF6, 0x87, 0xCB, 0x9E, 0x18,
    0x27, 0x96, 0x97, 0x90, 0x0E, 0x53, 0xFB, 0x31, 0x7C, 0x9C, 0xBC, 0xE4,
    0x8E, 0x23, 0xD0, 0x53, 0x71, 0xEC, 0xC1, 0x59, 0x51, 0xB8, 0xF3, 0x64,
    0x9D, 0x7C, 0xA3, 0x3E, 0xD6, 0x8D, 0xC9, 0x04, 0x7E, 0x82, 0xC9, 0xBA,
    0xAD, 0x97, 0x99, 0xD0, 0xD4, 0x58, 0xCB, 0x84, 0x7C, 0xA9, 0xFF, 0xBE,
    0x3C, 0x8A, 0x77, 0x52, 0x33, 0x55, 0x7D, 0xDE, 0x13, 0xA8, 0xB1, 0x40,
    0x87, 0xCC, 0x1B, 0xC8, 0xF1, 0x0F, 0x6E, 0xCD, 0xD0, 0x83, 0xA9, 0x59,
    0xCF, 0xF8, 0x4A, 0x9D, 0x1D, 0x50, 0x75, 0x5E, 0x3E, 0x19, 0x18, 0x18,
    0xAF, 0x23, 0xE2, 0x29, 0x35, 0x58, 0x76, 0x6D, 0x2C, 0x07, 0xE2, 0x57,
    0x12, 0xB2, 0xCA, 0x0B, 0x53, 0x5E, 0xD8, 0xF6, 0xC5, 0x6C, 0xE7, 0x3D,
    0x24, 0xBD, 0xD0, 0x29, 0x17, 0x71, 0x86, 0x1A, 0x54, 0xB4, 0xC2, 0x85,
    0xA9, 0xA3, 0xDB, 0x7A, 0xCA, 0x6D, 0x22, 0x4A, 0xEA, 0xCD, 0x62, 0x1D,
    0xB9, 0xF2, 0xA2, 0x2E, 0xD1, 0xE9, 0xE1, 0x1D, 0x75, 0xBE, 0xD7, 0xDC,
    0x0E, 0xCB, 0x0A, 0x8E, 0x68, 0xA2, 0xFF, 0x12, 0x63, 0x40, 0x8D, 0xC8,
    0x08, 0xDF, 0xFD, 0x16, 0x4B, 0x11, 0x67, 0x74, 0xCD, 0x0B, 0x9B, 0x8D,
    0x05, 0x41, 0x1E, 0xD6, 0x26, 0x2E, 0x42, 0x9B, 0xA4, 0x95, 0x67, 0x6B,
    0x83, 0x98, 0xDB, 0x2F, 0x35, 0xD3, 0xC1, 0xB9, 0xCE, 0xD5, 0x26, 0x36,
    0xF2, 0x76, 0x5E, 0x1A, 0x95, 0xCB, 0x7C, 0xA4, 0xC3, 0xDD, 0xAB, 0xDD,
    0xBF, 0xF3, 0x82, 0x53,
])

_TABLE_FPROT = bytes([
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0xF2, 0xAC,
    0xB4, 0x68, 0xC0, 0x86, 0xB6, 0xE3, 0xF6, 0x45,
    0xC8, 0x5D, 0xCF, 0x5E, 0xA2, 0xD6, 0xAE, 0x1A,
    0x13, 0x46, 0x5F, 0x3E, 0x99, 0x96, 0x7D, 0x57,
    0x7A, 0xF4, 0xDD, 0x34, 0xAF, 0x2A, 0x78, 0x92,
    0x9B, 0x35, 0x94, 0x98, 0xA3, 0x76, 0xF1, 0x44,
    0xE7, 0xB0, 0xBE, 0x3B, 0x0E, 0x14, 0xC5, 0x79,
    0x85, 0xCD, 0xC6, 0xF5, 0xEF, 0x8E, 0x15, 0xD7,
    0x77, 0x17, 0x89, 0xF8, 0xCA, 0x82, 0x5A, 0x32,
    0xBF, 0x3F, 0xFF, 0x2F, 0x6E, 0x88, 0xA6, 0x12,
    0x7B, 0xEC, 0x73, 0xE5, 0x58, 0x66, 0x52, 0x63,
    0x5C, 0xAB, 0xEB, 0x49, 0xD3, 0x0F, 0x3C, 0x3A,
    0x36, 0xA1, 0x18, 0xED, 0x27, 0x6B, 0xB8, 0x3D,
    0xC4, 0x6D, 0x4B, 0x2B, 0x91, 0xDB, 0x4D, 0x8B,
    0xA5, 0x83, 0x22, 0x1B, 0xDE, 0x87, 0xFC, 0xFD,
    0xBB, 0x8D, 0xE6, 0x7F, 0x6A, 0x26, 0x7C, 0xEE,
    0xB5, 0x9F, 0xE4, 0xE9, 0x69, 0x74, 0xC7, 0x56,
    0xF9, 0x39, 0x72, 0x23, 0xD8, 0x43, 0x25, 0x1F,
    0x4E, 0x61, 0x21, 0x33, 0xAD, 0x31, 0x64, 0xCC,
    0x51, 0x9E, 0xFA, 0xF3, 0xD4, 0xBA, 0xD5, 0x6F,
    0xF0, 0x7E, 0x1C, 0x29, 0xFB, 0x1D, 0x42, 0xE8,
    0x4A, 0x47, 0xAA, 0x90, 0x59, 0x67, 0x65, 0xB2,
    0x8A, 0x50, 0xDF, 0x9D, 0x53, 0xA8, 0x19, 0x71,
    0x54, 0x93, 0xA0, 0x2D, 0x24, 0x75, 0xE2, 0xFE,
    0xBD, 0x97, 0xA9, 0x95, 0xF7, 0x9A, 0xD9, 0x60,
    0x10, 0x2C, 0x40, 0x84, 0x2E, 0xC3, 0x41, 0xC1,
    0x6C, 0x38, 0x8C, 0xB9, 0x80, 0xDC, 0x1E, 0xCE,
    0xC2, 0x8F, 0xA4, 0xC9, 0xE1, 0x9C, 0x30, 0xD2,
    0x81, 0x28, 0xB7, 0xA7, 0xDA, 0x70, 0x5B, 0xCB,
    0xD1, 0xB1, 0x4F, 0x20, 0x11, 0x37, 0x48, 0xB3,
    0xE0, 0x16, 0x62, 0xEA, 0xBC, 0x55, 0x4C, 0xD0,
])


def _is_valid_output(data: bytes | bytearray | memoryview) -> bool:
    if len(data) < 2:
        return False
    from refinery.lib.id import get_structured_data_type
    return get_structured_data_type(data) is not None


def _handle_cmc(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:23]) != b'CMC Quarantined Malware':
        return None
    if len(view) < 0x200:
        return None
    fn_len = _u16(view, 0x50)
    filename = bytes(view[0x200:0x200 + fn_len]).rstrip(b'\x00').decode('latin1', errors='replace')
    tn_len = _u16(view, 0x6C)
    tn_off = 0x200 + fn_len
    threat = bytes(view[tn_off:tn_off + tn_len]).rstrip(b'\x00').decode('latin1', errors='replace')
    hdr_end = tn_off + tn_len
    if len(view) < hdr_end + 4:
        return None
    buf_len = _u32(view, hdr_end)
    payload_raw = bytes(view[hdr_end + 4:hdr_end + 4 + buf_len])
    import io
    import zipfile
    for payload in (_xor_byte(payload_raw, 30), payload_raw):
        try:
            with zipfile.ZipFile(io.BytesIO(payload)) as zf:
                for info in zf.infolist():
                    if not info.is_dir():
                        return QuarantineResult(zf.read(info.filename), 'CMC', filename, threat)
        except zipfile.BadZipFile:
            pass
    return None


def _handle_v3b(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:16]) != b'AhnLab Inc. 2006':
        return None
    if len(view) < 0x5C:
        return None
    payload_off = _u32(view, 0x58) + 0x58
    if payload_off >= len(view):
        return None
    payload = view[payload_off:]
    out = _xor_cyclic(payload, _KEY_V3B)
    if _is_valid_output(out):
        return QuarantineResult(out, 'AhnLab')
    raw_key = b'v3backup!@#$%^&)'
    out = _xor_cyclic(payload, raw_key)
    if _is_valid_output(out):
        return QuarantineResult(out, 'AhnLab')
    return None


def _handle_avira(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:11]) != b'AntiVir Qua':
        return None
    if len(view) < 20:
        return None
    payload_off = _u32(view, 16)
    if payload_off >= len(view):
        return None
    return QuarantineResult(_xor_byte(view[payload_off:], 0xAA), 'Avira')


def _handle_avast(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:8]) != b'-chest- ':
        return None
    data = view[8:]
    klen = len(_KEY_AVAST)
    out = bytearray(len(data))
    ki = 0
    for i in range(len(data)):
        out[i] = data[i] ^ _KEY_AVAST[ki]
        ki += 1
        if ki >= klen or (i % 0x10000) == 0xFFFF:
            ki = 0
    return QuarantineResult(out, 'Avast')


def _handle_forticlient(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:5]) != b'QUARF':
        return None
    if len(view) < 0x60:
        return None
    mal_len = _u32(view, 0x34)
    fn_len = _u32(view, 0x58)
    tn_len = _u32(view, 0x5C)
    fn_off = 0x60
    tn_off = fn_off + fn_len
    payload_off = tn_off + tn_len
    filename = None
    threat = None
    try:
        filename = bytes(view[fn_off:fn_off + fn_len]).decode('utf-16-le').rstrip('\x00')
    except Exception:
        pass
    try:
        threat = bytes(view[tn_off:tn_off + tn_len]).decode('utf-16-le').rstrip('\x00')
    except Exception:
        pass
    if payload_off + mal_len > len(view):
        mal_len = len(view) - payload_off
    if mal_len <= 0:
        return None
    return QuarantineResult(
        _xor_byte(view[payload_off:payload_off + mal_len], 0xAB),
        'FortiClient', filename, threat,
    )


def _handle_klq(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:4]) != b'KLQB':
        return None
    if len(view) < 0x34:
        return None
    header_len = _u32(view, 0x08)
    orig_len = _u32(view, 0x30)
    if header_len + orig_len > len(view):
        return None
    payload = view[header_len:header_len + orig_len]
    out = _xor_cyclic(payload, _KEY_KLQ)
    filename = None
    meta_off = _u32(view, 0x10)
    meta_len = _u32(view, 0x20)
    if meta_off + meta_len <= len(view):
        try:
            filename = _parse_klq_metadata(view, meta_off, meta_len)
        except Exception:
            pass
    return QuarantineResult(out, 'Kaspersky', filename)


def _parse_klq_metadata(
    view: memoryview,
    meta_off: int,
    meta_len: int,
) -> str | None:
    pos = meta_off
    end = meta_off + meta_len
    filename = None
    while pos + 4 < end:
        chunk_len = _u32(view, pos)
        if chunk_len == 0:
            break
        pos += 4
        if pos + chunk_len > end:
            break
        chunk = _xor_cyclic(view[pos:pos + chunk_len], _KEY_KLQ)
        if len(chunk) >= 4:
            id_len = _u32(chunk, 0)
            if 4 + id_len <= len(chunk):
                id_name = bytes(chunk[4:4 + id_len]).rstrip(b'\x00')
                id_val = chunk[4 + id_len:]
                if id_name == b'cNP_QB_FULLNAME' and id_val:
                    try:
                        filename = bytes(id_val).decode('utf-16-le').rstrip('\x00')
                    except Exception:
                        filename = bytes(id_val).replace(
                            b'\x00', b'').decode('latin1', errors='replace')
        pos += chunk_len
    return filename


def _vsbx_crc32_decrypt(payload: bytearray, base_key: int, data_off_raw: int) -> bytearray:
    unaligned = data_off_raw % 4
    off = data_off_raw - unaligned
    i = 0
    while i < len(payload):
        keyval = (base_key + off) & 0xFFFFFFFF
        crc_bytes = struct.pack('<I', zlib.crc32(struct.pack('<I', keyval)) & 0xFFFFFFFF)
        start = unaligned
        unaligned = 0
        for j in range(start, 4):
            if i >= len(payload):
                break
            payload[i] ^= crc_bytes[j]
            i += 1
        off += 4
    return payload


def _handle_vsbx(view: memoryview) -> QuarantineResult | None:
    if len(view) < 10:
        return None
    d32 = int.from_bytes(bytes(view[:4]), 'big')
    if d32 != 0xA9ACBDA7:
        return None
    decoded = _xor_byte(view, 0xFF)
    if bytes(decoded[:4]) != b'VSBX':
        return None
    data_off_raw = _u32(decoded, 4) + 10
    num_tags = _u16(decoded, 8)
    if num_tags > 15:
        return None
    base_key = 0
    enc_method = 0
    pos = 10
    for _ in range(num_tags):
        if pos + 3 > len(decoded):
            break
        code = decoded[pos]
        tag_len = _u16(decoded, pos + 1)
        pos += 3
        if pos + tag_len > len(decoded):
            break
        tag_data = decoded[pos:pos + tag_len]
        if code == 6 and tag_len >= 4:
            base_key = _u32(tag_data, 0)
        elif code == 7 and tag_len >= 4:
            enc_method = _u32(tag_data, 0)
        pos += tag_len
    if data_off_raw >= len(decoded):
        return None
    payload = bytearray(decoded[data_off_raw:])
    if enc_method == 2:
        _vsbx_crc32_decrypt(payload, base_key, data_off_raw)
    return QuarantineResult(bytes(payload), 'TrendMicro')


def _handle_gdata(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    if _u32(view, 0) != 0xCAFEBABE:
        return None
    hdr_len = _u32(view, 4)
    body_start = 8 + hdr_len
    if body_start + 8 > len(view):
        return None
    if _u32(view, body_start) != 0xBAADF00D:
        return None
    body_len = _u32(view, body_start + 4)
    payload_start = body_start + 8 + body_len
    if payload_start >= len(view):
        return None
    return QuarantineResult(_rc4(_KEY_GDATA, view[payload_start:]), 'G-Data')


def _handle_msdef_pc(view: memoryview) -> QuarantineResult | None:
    if len(view) < 0x3C:
        return None
    d16 = _u16(view, 0)
    if d16 == 0x0BAD:
        decrypted = _rc4(_KEY_MSDEF_PC, view)
        header_extra = _u32(decrypted, 8)
        payload_off = 0x28 + header_extra
        if payload_off >= len(decrypted):
            return None
        return QuarantineResult(bytes(decrypted[payload_off:]), 'Microsoft Defender')
    if d16 == 0xD345:
        header = bytearray(_rc4(_KEY_MSDEF_PC, view[:0x3C]))
        if _u32(header, 0) != 0x01C5E8DB:
            return None
        len1 = _u32(header, 0x28)
        len2 = _u32(header, 0x2C)
        dec1 = _rc4(_KEY_MSDEF_PC, view[0x3C:0x3C + len1])
        dec2 = _rc4(_KEY_MSDEF_PC, view[0x3C + len1:0x3C + len1 + len2])
        return QuarantineResult(bytes(header) + bytes(dec1) + bytes(dec2), 'Microsoft Defender')
    return None


def _handle_fprot(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:3]) != b'KSS':
        return None
    if len(view) < 8:
        return None
    data_off = _u32(view, 4) + 0xDC
    if data_off >= len(view):
        return None
    payload = view[data_off:]
    out = bytearray(len(payload))
    for i in range(len(payload)):
        out[i] = _TABLE_FPROT[payload[i]]
    return QuarantineResult(out, 'F-Prot')


def _handle_mcafee_bup(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:8]) != b'\xD0\xCF\x11\xE0\xA1\xB1\x1A\xE1':
        return None
    from refinery.lib.ole.file import OleFile
    try:
        with OleFile(view) as ole:
            for path in ole.listdir():
                joined = '/'.join(path)
                stream = ole.openstream(joined)
                content = _xor_byte(stream.read(), 0x6A)
                if _is_valid_output(content):
                    return QuarantineResult(content, 'McAfee')
    except Exception:
        pass
    return None


# --- BLIND HANDLERS ---


def _handle_bitdefender(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    out = bytearray(len(view))
    cl = 25
    dl = 43
    for i in range(len(view)):
        out[i] = ((view[i] - dl) & 0xFF) ^ cl
        cl = (cl + 3) & 0xFF
        dl = (dl + 20) & 0xFF
    return QuarantineResult(out, 'BitDefender')


def _handle_eset(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    out = bytearray(len(view))
    for i in range(len(view)):
        out[i] = ((view[i] - 84) & 0xFF) ^ 0xA5
    return QuarantineResult(out, 'ESET')


def _handle_k7(view: memoryview) -> QuarantineResult | None:
    if len(view) < 0x17C:
        return None
    payload_len = _u32(view, 0x128)
    payload_off = 0x178
    if payload_off + payload_len > len(view):
        payload_len = len(view) - payload_off
    if payload_len <= 0:
        return None
    return QuarantineResult(_xor_byte(view[payload_off:payload_off + payload_len], 0xFF), 'K7')


def _handle_malwarebytes(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    for phrase in (_PHRASE_MB_V1, _PHRASE_MB_V2):
        key = _md5(phrase)
        out = _rc4(key, view)
        if _is_valid_output(out):
            return QuarantineResult(out, 'MalwareBytes')
    return None


def _handle_symantec_vbn(view: memoryview) -> QuarantineResult | None:
    if len(view) < 0x12B8 + 16:
        return None
    data_off = _u32(view, 0)
    if data_off != 0x1290:
        return None
    decrypted = _xor_byte(view, 0x5A)
    offset = data_off + 0x28
    xor_next = False
    has_header = True
    for _ in range(20000):
        if offset + 1 >= len(decrypted):
            break
        code = decrypted[offset]
        offset += 1
        if code in (1, 10):
            tag_len = 2
            offset += tag_len
        elif code in (3, 6):
            if offset + 4 > len(decrypted):
                break
            tag_len = 5
            offset += tag_len
        elif code == 4:
            if offset + 8 > len(decrypted):
                break
            codeval = _u64(decrypted, offset)
            offset += 8
            if xor_next:
                has_header = False
            continue
        else:
            if offset + 4 > len(decrypted):
                break
            codeval = _u32(decrypted, offset)
            offset += 4
            if code == 9:
                if codeval == 0x08:
                    xor_next = True
                    offset += codeval
                    continue
                elif codeval == 0x10:
                    xor_next = False
                    offset += codeval
                    continue
                if xor_next and offset + codeval <= len(decrypted):
                    container = _xor_byte(decrypted[offset:offset + codeval], 0xFF)
                    if has_header and len(container) > 12:
                        hdr_len = _u32(container, 8)
                        if hdr_len < len(container):
                            payload = container[hdr_len:]
                            has_header = False
                            return QuarantineResult(bytes(payload), 'Symantec')
                    elif not has_header:
                        return QuarantineResult(bytes(container), 'Symantec')
            offset += codeval
    return None


def _handle_symantec_qfm(view: memoryview) -> QuarantineResult | None:
    if len(view) < 0x30:
        return None
    qfm_offset = _u32(view, 0)
    if qfm_offset == 0 or qfm_offset >= len(view):
        return None
    if view[qfm_offset] != 0x5A:
        return None
    decrypted = _xor_byte(view[qfm_offset:], 0x5A)
    if len(decrypted) < 0x20:
        return None
    total_size = _u64(decrypted, 0x18)
    struct1_offset = total_size
    if struct1_offset + 0x5A >= len(decrypted):
        return None
    s1 = decrypted[struct1_offset:]
    if len(s1) < 12 or s1[0] != 0x03 or s1[1] != 0x03:
        return None
    sha1_wide_size = _u32(s1, 8)
    if sha1_wide_size != 0x52:
        return None
    s1_size = 12 + sha1_wide_size
    s2_start = struct1_offset + s1_size
    if s2_start + 23 > len(decrypted):
        return None
    s2 = decrypted[s2_start:]
    original_file_size = _u64(s2, 15)
    s2_size = 23
    part3_start = s2_start + s2_size
    if part3_start >= len(decrypted):
        return None
    part3_byte = decrypted[part3_start]
    if part3_byte == 8:
        if part3_start + 5 >= len(decrypted):
            return None
        sd_size = _u32(decrypted, part3_start + 1)
        chunk_start = part3_start + 1 + 4 + sd_size + 5 + 1 + 8
    elif part3_byte == 9:
        chunk_start = part3_start
    else:
        return None
    if chunk_start >= len(decrypted):
        return None
    pos = chunk_start
    plain = bytearray()
    while len(plain) < original_file_size:
        if pos >= len(decrypted):
            break
        if decrypted[pos] != 9:
            break
        pos += 1
        if pos + 4 > len(decrypted):
            break
        size = _u32(decrypted, pos)
        pos += 4
        if pos + size > len(decrypted):
            break
        chunk = _xor_byte(decrypted[pos:pos + size], 0xFF)
        plain.extend(chunk)
        pos += size
    if not plain:
        return None
    return QuarantineResult(bytes(plain[:original_file_size]), 'Symantec')


def _handle_symantec_cc(view: memoryview) -> QuarantineResult | None:
    if len(view) < 33:
        return None
    key = bytes(view[16:32])
    payload = view[32:]
    out = _blowfish_ecb_swap(key, payload)
    return QuarantineResult(out, 'Symantec')


def _handle_symantec_qbd(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_byte(view, 0xB3), 'Symantec')


def _handle_panda(view: memoryview) -> QuarantineResult | None:
    if len(view) < 4:
        return None
    if bytes(view[:2]) != b'PK':
        return None
    import io
    import zipfile
    try:
        with zipfile.ZipFile(io.BytesIO(bytes(view))) as zf:
            for info in zf.infolist():
                if info.is_dir():
                    continue
                raw = zf.read(info.filename)
                dec = _blowfish_ecb(_KEY_PANDA, raw)
                try:
                    out = bytearray(zlib.decompress(dec, 16 + zlib.MAX_WBITS))
                except zlib.error:
                    try:
                        out = bytearray(zlib.decompress(dec))
                    except zlib.error:
                        continue
                if _is_valid_output(out):
                    return QuarantineResult(out, 'Panda')
    except zipfile.BadZipFile:
        pass
    return None


def _handle_baidu(view: memoryview) -> QuarantineResult | None:
    if len(view) < 0x58:
        return None
    pos = 0x52
    try:
        for _ in range(7):
            if pos + 4 > len(view):
                return None
            field_len = _u32(view, pos)
            pos += 4 + field_len
        if pos + 4 > len(view):
            return None
        pos += 4
    except Exception:
        return None
    out = bytearray()
    while pos + 2 <= len(view):
        chunk_len = _u16(view, pos)
        pos += 2
        if chunk_len == 0 or pos + chunk_len > len(view):
            break
        try:
            decompressed = bytearray(zlib.decompress(bytes(view[pos:pos + chunk_len])))
        except zlib.error:
            return None
        b = decompressed[0]
        for i in range(len(decompressed)):
            b ^= _KEY_BAIDU[i % 16]
        decompressed[0] = b
        out.extend(decompressed)
        pos += chunk_len
    if not out:
        return None
    return QuarantineResult(out, 'Baidu')


def _handle_amiti(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    key = bytes(b ^ 0xA4 for b in _KEY_AMITI)
    return QuarantineResult(_rc4(key, view), 'Amiti')


def _handle_asquared(view: memoryview) -> QuarantineResult | None:
    if len(view) < 0x42:
        return None
    data_start = 0x1A
    payload_off_field = _u32(view, data_start + 0x24)
    payload_off = data_start + payload_off_field
    if payload_off >= len(view):
        payload_off = data_start
    key = _md5(_GUID_ASQUARED_LC.decode('ascii').encode('utf-16-le'))
    return QuarantineResult(_rc4(key, view[payload_off:]), 'ASquared')


def _handle_zemana(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_rc4(_KEY_ZEMANA, view), 'Zemana')


def _handle_kserver(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    if _u16(view, 0) != 0xAF1F:
        return None
    return QuarantineResult(_xor_cyclic(view, _KEY_KLQ), 'Kaspersky Server')


def _handle_ksw(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_cyclic(view, _KEY_KSW), 'Kaspersky')


def _handle_quickheal(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    out = bytearray(len(view))
    for i in range(len(view)):
        b = view[i]
        out[i] = ((b >> 4) | (b << 4)) & 0xFF
    return QuarantineResult(out, 'QuickHeal')


def _handle_cisco_amp(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_byte(view, 0x77), 'Cisco AMP')


def _handle_sentinelone(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_byte(view, 0xFF), 'SentinelOne')


def _handle_ms_antimalware(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_byte(view, 0xFF), 'MS Antimalware')


def _handle_ms_def_mac(view: memoryview) -> QuarantineResult | None:
    if len(view) < 4:
        return None
    return QuarantineResult(_xor_byte(view, 0x25), 'MS Defender Mac')


def _handle_superantispyware(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_byte(view, 0xED), 'SUPERAntiSpyware')


def _handle_vipre(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    return QuarantineResult(_xor_byte(view, 0x33), 'Vipre')


def _handle_bullguard(view: memoryview) -> QuarantineResult | None:
    if len(view) < 16:
        return None
    out = bytearray(view)
    for i in range(0, len(out), 2):
        out[i] ^= 0x3F
    return QuarantineResult(out, 'BullGuard')


def _handle_esafe(view: memoryview) -> QuarantineResult | None:
    if len(view) < 4:
        return None
    import base64
    try:
        out = base64.b64decode(bytes(view))
    except Exception:
        return None
    return QuarantineResult(bytearray(out), 'eSafe')


def _handle_lumension(view: memoryview) -> QuarantineResult | None:
    if len(view) < 36:
        return None
    try:
        out = zlib.decompress(bytes(view[32:]), 16 + zlib.MAX_WBITS)
    except zlib.error:
        try:
            out = zlib.decompress(bytes(view[32:]))
        except zlib.error:
            return None
    return QuarantineResult(bytearray(out), 'Lumension')


def _handle_totalav(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:2]) != b'PK':
        return None
    import io
    import zipfile
    try:
        with zipfile.ZipFile(io.BytesIO(bytes(view))) as zf:
            for info in zf.infolist():
                if info.is_dir():
                    continue
                try:
                    data = zf.read(info.filename, pwd=b'infected')
                except (RuntimeError, zipfile.BadZipFile):
                    continue
                return QuarantineResult(bytearray(data), 'TotalAV', info.filename)
    except zipfile.BadZipFile:
        pass
    return None


def _handle_spybot(view: memoryview) -> QuarantineResult | None:
    if bytes(view[:2]) != b'PK':
        return None
    import io
    import zipfile
    try:
        with zipfile.ZipFile(io.BytesIO(bytes(view))) as zf:
            for info in zf.infolist():
                if info.is_dir():
                    continue
                try:
                    data = zf.read(info.filename, pwd=b'recovery')
                except (RuntimeError, zipfile.BadZipFile):
                    continue
                return QuarantineResult(bytearray(data), 'Spybot', info.filename)
    except zipfile.BadZipFile:
        pass
    return None


_MAGIC_HANDLERS = [
    _handle_cmc,
    _handle_v3b,
    _handle_avira,
    _handle_avast,
    _handle_forticlient,
    _handle_klq,
    _handle_vsbx,
    _handle_gdata,
    _handle_msdef_pc,
    _handle_fprot,
    _handle_mcafee_bup,
]

_BLIND_HANDLERS = [
    _handle_kserver,
    _handle_ms_def_mac,
    _handle_symantec_vbn,
    _handle_symantec_qfm,
    _handle_k7,
    _handle_totalav,
    _handle_spybot,
    _handle_panda,
    _handle_malwarebytes,
    _handle_symantec_cc,
    _handle_bitdefender,
    _handle_eset,
    _handle_baidu,
    _handle_amiti,
    _handle_asquared,
    _handle_zemana,
    _handle_symantec_qbd,
    _handle_quickheal,
    _handle_cisco_amp,
    _handle_sentinelone,
    _handle_ms_antimalware,
    _handle_superantispyware,
    _handle_vipre,
    _handle_bullguard,
    _handle_esafe,
    _handle_lumension,
    _handle_ksw,
]


class Vendor(str, enum.Enum):
    AhnLab           = 'ahnlab'           # noqa
    Amiti            = 'amiti'            # noqa
    ASquared         = 'asquared'         # noqa
    Avast            = 'avast'            # noqa
    Avira            = 'avira'            # noqa
    Baidu            = 'baidu'            # noqa
    BitDefender      = 'bitdefender'      # noqa
    BullGuard        = 'bullguard'        # noqa
    CiscoAMP         = 'ciscoamp'         # noqa
    CMC              = 'cmc'              # noqa
    ESafe            = 'esafe'            # noqa
    ESET             = 'eset'             # noqa
    FortiClient      = 'forticlient'      # noqa
    FProt            = 'fprot'            # noqa
    GData            = 'gdata'            # noqa
    K7               = 'k7'               # noqa
    Kaspersky        = 'kaspersky'        # noqa
    KasperskyServer  = 'kserver'          # noqa
    KasperskySW      = 'ksw'              # noqa
    Lumension        = 'lumension'        # noqa
    MalwareBytes     = 'malwarebytes'     # noqa
    McAfee           = 'mcafee'           # noqa
    MSDefender       = 'msdefender'       # noqa
    MSDefenderMac    = 'msdefmac'         # noqa
    MSAntimalware    = 'msamalware'       # noqa
    Panda            = 'panda'            # noqa
    QuickHeal        = 'quickheal'        # noqa
    SentinelOne      = 'sentinelone'      # noqa
    Spybot           = 'spybot'           # noqa
    SUPERAntiSpyware = 'superantispyware' # noqa
    SymantecVBN      = 'symvbn'           # noqa
    SymantecCC       = 'symcc'            # noqa
    SymantecQBD      = 'symqbd'           # noqa
    SymantecQFM      = 'symqfm'           # noqa
    TotalAV          = 'totalav'          # noqa
    TrendMicro       = 'trendmicro'       # noqa
    Vipre            = 'vipre'            # noqa
    Zemana           = 'zemana'           # noqa


_VENDOR_TO_HANDLER = {
    Vendor.AhnLab           : _handle_v3b,
    Vendor.Amiti            : _handle_amiti,
    Vendor.ASquared         : _handle_asquared,
    Vendor.Avast            : _handle_avast,
    Vendor.Avira            : _handle_avira,
    Vendor.Baidu            : _handle_baidu,
    Vendor.BitDefender      : _handle_bitdefender,
    Vendor.BullGuard        : _handle_bullguard,
    Vendor.CiscoAMP         : _handle_cisco_amp,
    Vendor.CMC              : _handle_cmc,
    Vendor.ESafe            : _handle_esafe,
    Vendor.ESET             : _handle_eset,
    Vendor.FortiClient      : _handle_forticlient,
    Vendor.FProt            : _handle_fprot,
    Vendor.GData            : _handle_gdata,
    Vendor.K7               : _handle_k7,
    Vendor.Kaspersky        : _handle_klq,
    Vendor.KasperskyServer  : _handle_kserver,
    Vendor.KasperskySW      : _handle_ksw,
    Vendor.Lumension        : _handle_lumension,
    Vendor.MalwareBytes     : _handle_malwarebytes,
    Vendor.McAfee           : _handle_mcafee_bup,
    Vendor.MSDefender       : _handle_msdef_pc,
    Vendor.MSDefenderMac    : _handle_ms_def_mac,
    Vendor.MSAntimalware    : _handle_ms_antimalware,
    Vendor.Panda            : _handle_panda,
    Vendor.QuickHeal        : _handle_quickheal,
    Vendor.SentinelOne      : _handle_sentinelone,
    Vendor.Spybot           : _handle_spybot,
    Vendor.SUPERAntiSpyware : _handle_superantispyware,
    Vendor.SymantecVBN      : _handle_symantec_vbn,
    Vendor.SymantecCC       : _handle_symantec_cc,
    Vendor.SymantecQBD      : _handle_symantec_qbd,
    Vendor.SymantecQFM      : _handle_symantec_qfm,
    Vendor.TotalAV          : _handle_totalav,
    Vendor.TrendMicro       : _handle_vsbx,
    Vendor.Vipre            : _handle_vipre,
    Vendor.Zemana           : _handle_zemana,
}


def unquarantine(
    data: bytes | bytearray | memoryview,
    vendor: Vendor | None = None,
) -> QuarantineResult:
    """
    Extract the original file from an antivirus quarantine container. If `vendor` is specified,
    only the handler for that vendor is tried. Otherwise, handlers with known magic are tried
    first, followed by blind handlers with output validation.
    """
    view = memoryview(bytearray(data))

    if vendor is not None:
        handler = _VENDOR_TO_HANDLER.get(vendor)
        if handler is None:
            raise ValueError(F'unknown quarantine vendor: {vendor}')
        result = handler(view)
        if result is not None:
            return result
        raise ValueError(
            F'the specified vendor format ({vendor}) could not decode this data')

    for handler in _MAGIC_HANDLERS:
        result = handler(view)
        if result is not None:
            return result

    for handler in _BLIND_HANDLERS:
        try:
            result = handler(view)
        except Exception:
            continue
        if result is not None and _is_valid_output(result.data):
            return result

    raise ValueError('no quarantine format matched this data')

Functions

def unquarantine(data, vendor=None)

Extract the original file from an antivirus quarantine container. If vendor is specified, only the handler for that vendor is tried. Otherwise, handlers with known magic are tried first, followed by blind handlers with output validation.

Expand source code Browse git
def unquarantine(
    data: bytes | bytearray | memoryview,
    vendor: Vendor | None = None,
) -> QuarantineResult:
    """
    Extract the original file from an antivirus quarantine container. If `vendor` is specified,
    only the handler for that vendor is tried. Otherwise, handlers with known magic are tried
    first, followed by blind handlers with output validation.
    """
    view = memoryview(bytearray(data))

    if vendor is not None:
        handler = _VENDOR_TO_HANDLER.get(vendor)
        if handler is None:
            raise ValueError(F'unknown quarantine vendor: {vendor}')
        result = handler(view)
        if result is not None:
            return result
        raise ValueError(
            F'the specified vendor format ({vendor}) could not decode this data')

    for handler in _MAGIC_HANDLERS:
        result = handler(view)
        if result is not None:
            return result

    for handler in _BLIND_HANDLERS:
        try:
            result = handler(view)
        except Exception:
            continue
        if result is not None and _is_valid_output(result.data):
            return result

    raise ValueError('no quarantine format matched this data')

Classes

class QuarantineResult (data, vendor, filename=None, threat=None)

QuarantineResult(data, vendor, filename, threat)

Expand source code Browse git
class QuarantineResult(NamedTuple):
    data: bytes | bytearray
    vendor: str
    filename: str | None = None
    threat: str | None = None

Ancestors

  • builtins.tuple

Instance variables

var data

Alias for field number 0

Expand source code Browse git
class QuarantineResult(NamedTuple):
    data: bytes | bytearray
    vendor: str
    filename: str | None = None
    threat: str | None = None
var vendor

Alias for field number 1

Expand source code Browse git
class QuarantineResult(NamedTuple):
    data: bytes | bytearray
    vendor: str
    filename: str | None = None
    threat: str | None = None
var filename

Alias for field number 2

Expand source code Browse git
class QuarantineResult(NamedTuple):
    data: bytes | bytearray
    vendor: str
    filename: str | None = None
    threat: str | None = None
var threat

Alias for field number 3

Expand source code Browse git
class QuarantineResult(NamedTuple):
    data: bytes | bytearray
    vendor: str
    filename: str | None = None
    threat: str | None = None
class Vendor (*args, **kwds)

str(object='') -> str str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or errors is specified, then the object must expose a data buffer that will be decoded using the given encoding and error handler. Otherwise, returns the result of object.str() (if defined) or repr(object). encoding defaults to 'utf-8'. errors defaults to 'strict'.

Expand source code Browse git
class Vendor(str, enum.Enum):
    AhnLab           = 'ahnlab'           # noqa
    Amiti            = 'amiti'            # noqa
    ASquared         = 'asquared'         # noqa
    Avast            = 'avast'            # noqa
    Avira            = 'avira'            # noqa
    Baidu            = 'baidu'            # noqa
    BitDefender      = 'bitdefender'      # noqa
    BullGuard        = 'bullguard'        # noqa
    CiscoAMP         = 'ciscoamp'         # noqa
    CMC              = 'cmc'              # noqa
    ESafe            = 'esafe'            # noqa
    ESET             = 'eset'             # noqa
    FortiClient      = 'forticlient'      # noqa
    FProt            = 'fprot'            # noqa
    GData            = 'gdata'            # noqa
    K7               = 'k7'               # noqa
    Kaspersky        = 'kaspersky'        # noqa
    KasperskyServer  = 'kserver'          # noqa
    KasperskySW      = 'ksw'              # noqa
    Lumension        = 'lumension'        # noqa
    MalwareBytes     = 'malwarebytes'     # noqa
    McAfee           = 'mcafee'           # noqa
    MSDefender       = 'msdefender'       # noqa
    MSDefenderMac    = 'msdefmac'         # noqa
    MSAntimalware    = 'msamalware'       # noqa
    Panda            = 'panda'            # noqa
    QuickHeal        = 'quickheal'        # noqa
    SentinelOne      = 'sentinelone'      # noqa
    Spybot           = 'spybot'           # noqa
    SUPERAntiSpyware = 'superantispyware' # noqa
    SymantecVBN      = 'symvbn'           # noqa
    SymantecCC       = 'symcc'            # noqa
    SymantecQBD      = 'symqbd'           # noqa
    SymantecQFM      = 'symqfm'           # noqa
    TotalAV          = 'totalav'          # noqa
    TrendMicro       = 'trendmicro'       # noqa
    Vipre            = 'vipre'            # noqa
    Zemana           = 'zemana'           # noqa

Ancestors

  • builtins.str
  • enum.Enum

Class variables

var AhnLab

The type of the None singleton.

var Amiti

The type of the None singleton.

var ASquared

The type of the None singleton.

var Avast

The type of the None singleton.

var Avira

The type of the None singleton.

var Baidu

The type of the None singleton.

var BitDefender

The type of the None singleton.

var BullGuard

The type of the None singleton.

var CiscoAMP

The type of the None singleton.

var CMC

The type of the None singleton.

var ESafe

The type of the None singleton.

var ESET

The type of the None singleton.

var FortiClient

The type of the None singleton.

var FProt

The type of the None singleton.

var GData

The type of the None singleton.

var K7

The type of the None singleton.

var Kaspersky

The type of the None singleton.

var KasperskyServer

The type of the None singleton.

var KasperskySW

The type of the None singleton.

var Lumension

The type of the None singleton.

var MalwareBytes

The type of the None singleton.

var McAfee

The type of the None singleton.

var MSDefender

The type of the None singleton.

var MSDefenderMac

The type of the None singleton.

var MSAntimalware

The type of the None singleton.

var Panda

The type of the None singleton.

var QuickHeal

The type of the None singleton.

var SentinelOne

The type of the None singleton.

var Spybot

The type of the None singleton.

var SUPERAntiSpyware

The type of the None singleton.

var SymantecVBN

The type of the None singleton.

var SymantecCC

The type of the None singleton.

var SymantecQBD

The type of the None singleton.

var SymantecQFM

The type of the None singleton.

var TotalAV

The type of the None singleton.

var TrendMicro

The type of the None singleton.

var Vipre

The type of the None singleton.

var Zemana

The type of the None singleton.