吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 474|回复: 3
上一主题 下一主题
收起左侧

[Python 原创] WinCmd.Key 验证器 (Total Commander 许可证验证器)

[复制链接]
跳转到指定楼层
楼主
shieep 发表于 2026-9-4 19:18 回帖奖励
分析wincm.key,提取信息,分析结构,验证黑名单,需要11.58版本的TOTALCMD64.EXE放在同一目录。
[Python] 纯文本查看 复制代码
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
WinCmd.Key 验证器 (Total Commander 许可证验证器)
基于 [url]https://www.cnblogs.com/DirWang/p/21385528[/url] 文章分析实现

功能:
  - 解析 wincmd.key 文件结构
  - LUC 签名验证 (Lucas 序列 Montgomery ladder)
  - License_Cipher 对称加解密
  - custom_md5 哈希校验
  - 提取并显示注册信息: 用户名、订单号、用户数量、日期、地区代码等
  - 自动从脚本目录下的 TOTALCMD64.EXE 读取内置 LUC 模数 N
"""

import os
import sys
import struct
import math
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext

# ============================================================
# 常量定义 (与文章一致)
# ============================================================
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
BINARY = os.path.join(SCRIPT_DIR, "TOTALCMD64.EXE")

IMAGE_BASE = 0x400000

# wincmd.key 文件结构
PART0_OFF, PART0_LEN = 0x000, 0x080
PART1_OFF, PART1_LEN = 0x080, 0x080
PART2_OFF, PART2_LEN = 0x100, 0x100
PART3_OFF, PART3_LEN = 0x200, 0x100
PART4_OFF, PART4_LEN = 0x300, 0x080
PART5_OFF, PART5_LEN = 0x380, 0x070
PART6_OFF, PART6_LEN = 0x3F0, 0x010

LUC_SIG_OFF, LUC_SIG_LEN = 0x080, 0x68
LUC_RESULT_LEN = 0x67
LUC_HASH_OFF, LUC_HASH_LEN = 0x00, 0x10
LUC_DATA_OFF, LUC_DATA_LEN = 0x10, 0x57

CIPHER_LEN = 0xFF
CIPHER_ROUNDS = 0x14
CIPHER_SEED_1 = 0x5862CF
CIPHER_SEED_2 = 0x1E0F5
CIPHER_SEED_LUC = 0x20A4BC
CIPHER_SEED_PART6 = 0x8800
LCG_MUL = 0x8088405
LCG_INC = 1
LCG_MASK = 0xFFFFFFFF

LUC_N_VA = 0xC500F8
LUC_N_HEX_LEN = 208
LUC_E = 65537

# 黑名单列表 VA
PIRATED_KEYS_LIST1_VA = 0xC4ECB0
PIRATED_KEYS_LIST1_COUNT = 209
PIRATED_KEYS_LIST2_VA = 0xC4EE60
PIRATED_KEYS_LIST2_COUNT = 528
VALID_MASTER_KEYS_VA = 0xC4F6A0
VALID_MASTER_KEYS_COUNT = 29
REVOKED_KEYS_BLACKLIST_VA = 0xC4F720
REVOKED_KEYS_BLACKLIST_COUNT = 549

# Part 5 固定版权字符串
PART5_CONTENT = (
    b'Copyright \xa9 1999 by Christian Ghisler, C. Ghisler & Co., '
    b'all rights reserved. Unauthorized copying prohibited!\x00\x88'
)

# ============================================================
# PE 区段映射: VA -> 文件偏移
# ============================================================
def _read_pe_sections(f):
    f.seek(0x3C)
    pe_off = struct.unpack('<I', f.read(4))[0]
    f.seek(pe_off + 4)
    _, num_sections, _, _, _, opt_size, _ = struct.unpack('<HHIIIHH', f.read(20))
    sec_start = pe_off + 24 + opt_size
    f.seek(sec_start)
    sections = []
    for _ in range(num_sections):
        name = f.read(8).rstrip(b'\x00').decode('ascii', errors='replace')
        vsize, vaddr, raw_size, raw_addr = struct.unpack('<IIII', f.read(16))
        f.read(16)
        sections.append((name, vaddr, vsize, raw_addr, raw_size))
    return sections

def va_to_file_offset(f, va):
    rva = va - IMAGE_BASE
    f.seek(0)
    sections = _read_pe_sections(f)
    for name, vaddr, vsize, raw_addr, raw_size in sections:
        if vaddr <= rva < vaddr + vsize:
            return raw_addr + (rva - vaddr)
    raise ValueError(f"VA 0x{va:X} (RVA 0x{rva:X}) 不在任何区段内")

# ============================================================
# LCG PRNG + 对称加解密
# ============================================================
def lcg_next(state, max_val):
    state = (LCG_MUL * state + LCG_INC) & LCG_MASK
    value = (max_val * state) >> 32
    return state, value

def ror_byte(val, n):
    n &= 7
    return ((val >> n) | (val << (8 - n))) & 0xFF

def rol_byte(val, n):
    n &= 7
    return ((val << n) | (val >> (8 - n))) & 0xFF

def cipher_encrypt(buf, seed, length=CIPHER_LEN):
    buf = bytearray(buf[:length + 1])
    state = seed & LCG_MASK
    for _ in range(CIPHER_ROUNDS):
        for i in range(length + 1):
            state, pos = lcg_next(state, length + 1)
            buf[i], buf[pos] = buf[pos], buf[i]
        for i in range(length + 1):
            state, rot = lcg_next(state, 8)
            buf[i] = ror_byte(buf[i], rot)
            state, xor_val = lcg_next(state, 0x100)
            buf[i] ^= xor_val
    return bytes(buf)

def cipher_decrypt(buf, seed, length=CIPHER_LEN):
    buf = bytearray(buf[:length + 1])
    state = seed & LCG_MASK
    rounds = []
    for _ in range(CIPHER_ROUNDS):
        p1 = []
        for i in range(length + 1):
            state, pos = lcg_next(state, length + 1)
            p1.append(pos)
        p2 = []
        for i in range(length + 1):
            state, rot = lcg_next(state, 8)
            state, xor_val = lcg_next(state, 0x100)
            p2.append((rot, xor_val))
        rounds.append((p1, p2))
    for p1, p2 in reversed(rounds):
        for i in reversed(range(length + 1)):
            rot, xor_val = p2[i]
            buf[i] ^= xor_val
            buf[i] = rol_byte(buf[i], rot)
        for i in reversed(range(length + 1)):
            pos = p1[i]
            buf[i], buf[pos] = buf[pos], buf[i]
    return bytes(buf)

# ============================================================
# custom_md5 (非标准 MD5)
# ============================================================
_MD5_EXTRA_PADDING = bytes((3 * i) & 0xFF for i in range(64))

def _md5_compress(state, block):
    a, b, c, d = state
    M = struct.unpack('<16I', block)
    T = [int(abs(math.sin(i + 1)) * 2**32) & 0xFFFFFFFF for i in range(64)]
    S = ([7, 12, 17, 22] * 4 + [5, 9, 14, 20] * 4 +
         [4, 11, 16, 23] * 4 + [6, 10, 15, 21] * 4)

    def rol(x, n):
        x &= 0xFFFFFFFF
        return ((x << n) | (x >> (32 - n))) & 0xFFFFFFFF

    for i in range(16):
        f = (b & c) | (~b & d)
        g = i
        a = rol((a + f + T[i] + M[g]) & 0xFFFFFFFF, S[i])
        a = (a + b) & 0xFFFFFFFF
        a, b, c, d = d, a, b, c
    for i in range(16, 32):
        f = (d & b) | (~d & c)
        g = (5 * i + 1) % 16
        a = rol((a + f + T[i] + M[g]) & 0xFFFFFFFF, S[i])
        a = (a + b) & 0xFFFFFFFF
        a, b, c, d = d, a, b, c
    for i in range(32, 48):
        f = b ^ c ^ d
        g = (3 * i + 5) % 16
        a = rol((a + f + T[i] + M[g]) & 0xFFFFFFFF, S[i])
        a = (a + b) & 0xFFFFFFFF
        a, b, c, d = d, a, b, c
    for i in range(48, 64):
        f = c ^ (b | (~d & 0xFFFFFFFF))
        g = (7 * i) % 16
        a = rol((a + f + T[i] + M[g]) & 0xFFFFFFFF, S[i])
        a = (a + b) & 0xFFFFFFFF
        a, b, c, d = d, a, b, c

    return [(state[0] + a) & 0xFFFFFFFF, (state[1] + b) & 0xFFFFFFFF,
            (state[2] + c) & 0xFFFFFFFF, (state[3] + d) & 0xFFFFFFFF]

def custom_md5(data):
    state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476]
    msg_len = len(data)
    bit_count = msg_len * 8
    padding = bytes([0x80])
    pad_len = (56 - (msg_len + 1) % 64) % 64
    padding += bytes(pad_len)
    padding += struct.pack('<Q', bit_count)
    all_data = data + padding + _MD5_EXTRA_PADDING
    for i in range(0, len(all_data), 64):
        state = _md5_compress(state, all_data[i:i + 64])
    return struct.pack('<4I', *state)

# ============================================================
# LUC 密码系统
# ============================================================
def lucas_v(n, P, N):
    if n == 0:
        return 2 % N
    P = P % N
    v0, v1 = 2, P
    for bit in bin(n)[2:]:
        if bit == '0':
            v0, v1 = (v0 * v0 - 2) % N, (v0 * v1 - P) % N
        else:
            v0, v1 = (v0 * v1 - P) % N, (v1 * v1 - 2) % N
    return v0

# ============================================================
# 公钥解析
# ============================================================
def parse_public_key(binary_path):
    with open(binary_path, "rb") as f:
        n_off = va_to_file_offset(f, LUC_N_VA)
        f.seek(n_off)
        n_hex = f.read(LUC_N_HEX_LEN).decode('ascii')
    n_int = int(n_hex, 16)
    return n_int

# ============================================================
# 黑名单读取
# ============================================================
def read_license_lists(binary_path):
    with open(binary_path, "rb") as f:
        off1 = va_to_file_offset(f, PIRATED_KEYS_LIST1_VA)
        f.seek(off1)
        data1 = f.read(PIRATED_KEYS_LIST1_COUNT * 2)
        list1 = list(struct.unpack(f'<{PIRATED_KEYS_LIST1_COUNT}H', data1))

        off2 = va_to_file_offset(f, PIRATED_KEYS_LIST2_VA)
        f.seek(off2)
        data2 = f.read(PIRATED_KEYS_LIST2_COUNT * 4)
        list2 = list(struct.unpack(f'<{PIRATED_KEYS_LIST2_COUNT}I', data2))

        off3 = va_to_file_offset(f, VALID_MASTER_KEYS_VA)
        f.seek(off3)
        data3 = f.read(VALID_MASTER_KEYS_COUNT * 4)
        list3 = list(struct.unpack(f'<{VALID_MASTER_KEYS_COUNT}I', data3))

        off4 = va_to_file_offset(f, REVOKED_KEYS_BLACKLIST_VA)
        f.seek(off4)
        data4 = f.read(REVOKED_KEYS_BLACKLIST_COUNT * 4)
        list4 = list(struct.unpack(f'<{REVOKED_KEYS_BLACKLIST_COUNT}I', data4))

    return {
        "pirated_list1": list1,
        "pirated_list2": list2,
        "valid_master_keys": list3,
        "revoked_blacklist": list4,
    }

def check_license_lists(license_num, lists, user_count=None):
    in_list1 = license_num in lists["pirated_list1"]
    in_list2 = license_num in lists["pirated_list2"]
    in_list3 = license_num in lists["valid_master_keys"]
    in_list4 = license_num in lists["revoked_blacklist"]
    special_invalid = (license_num == 0xFFFFFF)
    if user_count is not None:
        special_invalid = special_invalid or (user_count == 0xFFFF)

    if in_list4:
        state_str = "已吊销 (revoked)"
    elif in_list3:
        state_str = "预注册主密钥 (黑名单)"
    elif in_list1 or in_list2:
        state_str = "盗版密钥 (pirated)"
    else:
        state_str = "正常注册"

    license_invalid = in_list1 or in_list2 or in_list3 or in_list4 or special_invalid
    return {
        "in_pirated_list1": in_list1,
        "in_pirated_list2": in_list2,
        "in_valid_master_keys": in_list3,
        "in_revoked_blacklist": in_list4,
        "special_invalid": special_invalid,
        "license_invalid": license_invalid,
        "state_str": state_str,
    }

# ============================================================
# FAT 日期转换
# ============================================================
def fat_date_to_string(date_field):
    day = date_field & 0x1F
    month = (date_field >> 5) & 0x0F
    year = (date_field >> 9) + 1980
    if month == 0 or day == 0:
        return None
    try:
        import datetime
        datetime.date(year, month, day)
        return f"{year:04d}-{month:02d}-{day:02d}"
    except ValueError:
        return None

def grace_period_range(date_str):
    try:
        import datetime
        d = datetime.datetime.strptime(date_str, "%Y-%m-%d").date()
        lower = d - datetime.timedelta(days=1)
        upper = d + datetime.timedelta(days=31)
        return f"{lower.strftime('%Y-%m-%d')} ~ {upper.strftime('%Y-%m-%d')}"
    except (ValueError, TypeError):
        return None

# ============================================================
# 核心解析函数
# ============================================================
def parse_license(key_path, binary_path=None):
    with open(key_path, "rb") as f:
        data = f.read()

    if len(data) != 0x400:
        raise ValueError(f"密钥文件大小应为 0x400 字节, 实际为 0x{len(data):X}")

    part0 = data[PART0_OFF:PART0_OFF + PART0_LEN]
    part1 = data[PART1_OFF:PART1_OFF + PART1_LEN]
    part2 = data[PART2_OFF:PART2_OFF + PART2_LEN]
    part3 = data[PART3_OFF:PART3_OFF + PART3_LEN]
    part4 = data[PART4_OFF:PART4_OFF + PART4_LEN]
    part5 = data[PART5_OFF:PART5_OFF + PART5_LEN]
    part6 = data[PART6_OFF:PART6_OFF + PART6_LEN]

    luc_sig_bytes = data[LUC_SIG_OFF:LUC_SIG_OFF + LUC_SIG_LEN]
    luc_sig_int = int.from_bytes(luc_sig_bytes, 'little')

    buf1 = cipher_encrypt(data[0x100:0x200], CIPHER_SEED_1)
    elgamal_hash_data = buf1[0x20:0x20 + 0x78]
    elgamal_r_bytes = buf1[0x98:0x98 + 0x68]
    buf2 = cipher_encrypt(data[0x200:0x300], CIPHER_SEED_2)
    elgamal_s_bytes = buf2[0x00:0x00 + 0x68]
    elgamal_r_int = int.from_bytes(elgamal_r_bytes, 'little')
    elgamal_s_int = int.from_bytes(elgamal_s_bytes, 'little')

    part5_md5 = custom_md5(part5)
    part6_decrypted = cipher_encrypt(part6, CIPHER_SEED_PART6, 0xF)
    hash2_valid = (part5_md5 == part6_decrypted)

    luc_result_bytes = None
    luc_result_encrypted = None
    hash1_valid = None
    license_num = None
    user_count = None
    user_info = None
    region_code = None
    date_field = None
    license_type_str = None
    V_e_S = None
    blacklist_check = None
    special_invalid = None
    final_valid = False
    N = None

    binary_exists = binary_path and os.path.exists(binary_path)

    try:
        if binary_exists:
            N_hex = parse_public_key(binary_path)
            N = N_hex + 2
            V_e_S = lucas_v(LUC_E, luc_sig_int, N)
            mask = (1 << (LUC_RESULT_LEN * 8)) - 1
            luc_result_bytes = (V_e_S & mask).to_bytes(LUC_RESULT_LEN, 'little')
            luc_result_encrypted = bytearray(cipher_encrypt(luc_result_bytes, CIPHER_SEED_LUC, 0x66))
            expected_hash = bytes(luc_result_encrypted[LUC_HASH_OFF:LUC_HASH_OFF + LUC_HASH_LEN])
            hash_input = bytes(luc_result_encrypted[LUC_DATA_OFF:LUC_DATA_OFF + LUC_DATA_LEN])
            computed_hash = custom_md5(hash_input)
            hash1_valid = (computed_hash == expected_hash)

            lic_hi = luc_result_encrypted[0x10]
            lic_lo = int.from_bytes(luc_result_encrypted[0x11:0x13], 'little')
            license_num = (lic_hi << 16) + lic_lo
            user_count = int.from_bytes(luc_result_encrypted[0x13:0x15], 'little')
            date_field = int.from_bytes(luc_result_encrypted[0x15:0x17], 'little')
            suffix_bytes = bytes(luc_result_encrypted[0x17:0x1A])
            suffix_str = suffix_bytes.split(b'\x00')[0]
            if suffix_str and all(0x20 <= b < 0x7F for b in suffix_str):
                region_code = suffix_str.decode('ascii')
            else:
                region_code = suffix_str.hex() if suffix_str else ''
            user_info_bytes = bytes(luc_result_encrypted[0x1A:LUC_RESULT_LEN])
            user_info = user_info_bytes.split(b'\x00')[0].decode('latin-1', errors='replace')
            if user_count == 1:
                license_type_str = "Single user licence"
            elif user_count > 1:
                license_type_str = f"{user_count} User licence"
            else:
                license_type_str = f"(invalid user_count={user_count})"

            lists = read_license_lists(binary_path)
            blacklist_check = check_license_lists(license_num, lists, user_count)
            special_invalid = (user_count == 0xFFFF or license_num == 0xFFFFFF)

            final_valid = hash1_valid and hash2_valid
            if blacklist_check["license_invalid"]:
                final_valid = False
            if special_invalid:
                final_valid = False
        else:
            final_valid = False

    except Exception as e:
        import traceback
        traceback.print_exc()

    return {
        "part0": part0, "part1": part1, "part2": part2,
        "part3": part3, "part4": part4, "part5": part5, "part6": part6,
        "luc_sig_bytes": luc_sig_bytes, "luc_sig_int": luc_sig_int,
        "V_e_S": V_e_S, "luc_result_bytes": luc_result_bytes,
        "luc_result_encrypted": luc_result_encrypted,
        "hash1_valid": hash1_valid, "hash2_valid": hash2_valid,
        "part5_md5": part5_md5, "part6_decrypted": part6_decrypted,
        "license_num": license_num, "user_count": user_count,
        "user_info": user_info, "region_code": region_code,
        "date_field": date_field, "license_type_str": license_type_str,
        "elgamal_hash_data": elgamal_hash_data,
        "elgamal_r_int": elgamal_r_int, "elgamal_s_int": elgamal_s_int,
        "blacklist_check": blacklist_check,
        "special_invalid": special_invalid,
        "final_valid": final_valid,
        "N": N,
        "binary_exists": binary_exists,
    }


# ============================================================
# GUI 界面
# ============================================================
class WinCmdKeyValidator(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("WinCmd.Key 验证器 - Total Commander 许可证分析")
        self.geometry("900x700")
        self.minsize(800, 600)

        self.key_path = tk.StringVar()
        self.result_data = None

        self._build_ui()

    def _build_ui(self):
        # 文件选择区
        file_frame = ttk.LabelFrame(self, text="文件选择", padding=10)
        file_frame.pack(fill=tk.X, padx=10, pady=5)

        ttk.Label(file_frame, text="wincmd.key:").grid(row=0, column=0, sticky=tk.W, pady=3)
        ttk.Entry(file_frame, textvariable=self.key_path, width=70).grid(row=0, column=1, padx=5, pady=3)
        ttk.Button(file_frame, text="浏览...", command=self._browse_key).grid(row=0, column=2, pady=3)

        # 显示自动检测的 exe 路径
        ttk.Label(file_frame, text=f"TOTALCMD64.EXE: {BINARY}").grid(row=1, column=0, columnspan=3, sticky=tk.W, pady=3)

        ttk.Button(file_frame, text="开始验证", command=self._validate).grid(row=2, column=0, columnspan=3, pady=10)

        # 结果摘要
        self.summary_frame = ttk.LabelFrame(self, text="验证结果摘要", padding=10)
        self.summary_frame.pack(fill=tk.X, padx=10, pady=5)
        self.summary_label = ttk.Label(self.summary_frame, text="请选择 wincmd.key 并点击「开始验证」", font=("Microsoft YaHei", 12))
        self.summary_label.pack(anchor=tk.W)

        # Notebook 标签页
        self.notebook = ttk.Notebook(self)
        self.notebook.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        # 标签页 1: 注册信息
        self.tab_info = ttk.Frame(self.notebook)
        self.notebook.add(self.tab_info, text="注册信息")
        self._build_info_tab()

        # 标签页 2: 验证详情
        self.tab_verify = ttk.Frame(self.notebook)
        self.notebook.add(self.tab_verify, text="验证详情")
        self._build_verify_tab()

        # 标签页 3: 文件结构
        self.tab_struct = ttk.Frame(self.notebook)
        self.notebook.add(self.tab_struct, text="文件结构")
        self._build_struct_tab()

        # 标签页 4: 黑名单
        self.tab_blacklist = ttk.Frame(self.notebook)
        self.notebook.add(self.tab_blacklist, text="黑名单检查")
        self._build_blacklist_tab()

        # 状态栏
        self.status = ttk.Label(self, text="就绪", relief=tk.SUNKEN, anchor=tk.W)
        self.status.pack(fill=tk.X, side=tk.BOTTOM)

    def _build_info_tab(self):
        frame = ttk.Frame(self.tab_info, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)

        fields = [
            ("注册状态", "final_valid"),
            ("用户名", "user_info"),
            ("许可证号", "license_num"),
            ("用户数量", "user_count"),
            ("许可证类型", "license_type_str"),
            ("日期字段", "date_field"),
            ("地区代码", "region_code"),
            ("显示格式", "display_str"),
        ]
        self.info_vars = {}
        for i, (label, key) in enumerate(fields):
            ttk.Label(frame, text=f"{label}:", font=("Microsoft YaHei", 10, "bold")).grid(row=i, column=0, sticky=tk.W, pady=4, padx=5)
            var = tk.StringVar(value="-")
            self.info_vars[key] = var
            ttk.Label(frame, textvariable=var, font=("Consolas", 10)).grid(row=i, column=1, sticky=tk.W, pady=4, padx=5)
        frame.columnconfigure(1, weight=1)

    def _build_verify_tab(self):
        frame = ttk.Frame(self.tab_verify, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)

        self.verify_text = scrolledtext.ScrolledText(frame, wrap=tk.WORD, font=("Consolas", 10))
        self.verify_text.pack(fill=tk.BOTH, expand=True)
        self.verify_text.insert(tk.END, "等待验证...")
        self.verify_text.config(state=tk.DISABLED)

    def _build_struct_tab(self):
        frame = ttk.Frame(self.tab_struct, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)

        self.struct_text = scrolledtext.ScrolledText(frame, wrap=tk.WORD, font=("Consolas", 9))
        self.struct_text.pack(fill=tk.BOTH, expand=True)
        self.struct_text.insert(tk.END, "等待验证...")
        self.struct_text.config(state=tk.DISABLED)

    def _build_blacklist_tab(self):
        frame = ttk.Frame(self.tab_blacklist, padding=10)
        frame.pack(fill=tk.BOTH, expand=True)

        self.blacklist_text = scrolledtext.ScrolledText(frame, wrap=tk.WORD, font=("Consolas", 10))
        self.blacklist_text.pack(fill=tk.BOTH, expand=True)
        self.blacklist_text.insert(tk.END, "等待验证...")
        self.blacklist_text.config(state=tk.DISABLED)

    def _browse_key(self):
        path = filedialog.askopenfilename(title="选择 wincmd.key", filetypes=[("Key files", "*.key"), ("All files", "*.*")])
        if path:
            self.key_path.set(path)

    def _set_text(self, widget, text):
        widget.config(state=tk.NORMAL)
        widget.delete(1.0, tk.END)
        widget.insert(tk.END, text)
        widget.config(state=tk.DISABLED)

    def _validate(self):
        key_path = self.key_path.get().strip()

        if not key_path:
            messagebox.showerror("错误", "请选择 wincmd.key 文件")
            return
        if not os.path.exists(key_path):
            messagebox.showerror("错误", f"找不到文件: {key_path}")
            return

        try:
            self.status.config(text="正在解析...")
            self.update_idletasks()

            info = parse_license(key_path, BINARY)
            self.result_data = info

            if info["binary_exists"]:
                if info["final_valid"]:
                    self.summary_label.config(text="&#9989; 许可证验证通过", foreground="green")
                else:
                    self.summary_label.config(text="&#10060; 许可证验证失败", foreground="red")
            else:
                self.summary_label.config(text=f"&#9888;&#65039; 未找到 {BINARY},仅解析文件结构(无法验证签名)", foreground="orange")

            # 注册信息
            self.info_vars["final_valid"].set("有效" if info["final_valid"] else "无效" if info["binary_exists"] else "未验证")
            self.info_vars["user_info"].set(info["user_info"] if info["user_info"] else "-")
            self.info_vars["license_num"].set(str(info["license_num"]) if info["license_num"] is not None else "-")
            self.info_vars["user_count"].set(str(info["user_count"]) if info["user_count"] is not None else "-")
            self.info_vars["license_type_str"].set(info["license_type_str"] if info["license_type_str"] else "-")
            date_str = fat_date_to_string(info["date_field"]) if info["date_field"] is not None else None
            self.info_vars["date_field"].set(f"{info['date_field']} ({date_str})" if date_str else str(info["date_field"]) if info["date_field"] is not None else "-")
            self.info_vars["region_code"].set(info["region_code"] if info["region_code"] else "-")
            display = ""
            if info["user_info"] and info["region_code"] and info["license_type_str"]:
                display = f"Total Commander (x64) - {info['user_info']} ({info['region_code']})\n\n#{info['license_num']} {info['license_type_str']}"
            self.info_vars["display_str"].set(display)

            # 验证详情
            verify_lines = []
            verify_lines.append("=" * 60)
            verify_lines.append("LUC 签名验证")
            verify_lines.append("=" * 60)
            if info["binary_exists"]:
                verify_lines.append(f"LUC 模数 N: {info['N']}")
                verify_lines.append(f"LUC 签名 S 位长度: {info['luc_sig_int'].bit_length()}")
                verify_lines.append(f"V_65537(S) mod N 位长度: {info['V_e_S'].bit_length() if info['V_e_S'] else 'N/A'}")
                verify_lines.append("")
                verify_lines.append("第一次哈希校验 (LUC 路径)")
                verify_lines.append("-" * 40)
                if info["luc_result_encrypted"] is not None:
                    scr = info["luc_result_encrypted"]
                    expected_hash = bytes(scr[0x00:0x10])
                    hash_input = bytes(scr[0x10:0x67])
                    computed_hash = custom_md5(hash_input)
                    verify_lines.append(f"期望哈希: {expected_hash.hex()}")
                    verify_lines.append(f"计算哈希: {computed_hash.hex()}")
                    verify_lines.append(f"结果: {'通过 &#9989;' if info['hash1_valid'] else '失败 &#10060;'}")
                else:
                    verify_lines.append("无法计算 (LUC 结果为空)")
            else:
                verify_lines.append(f"未找到 {BINARY},跳过 LUC 验证")
            verify_lines.append("")
            verify_lines.append("第二次哈希校验 (反篡改)")
            verify_lines.append("-" * 40)
            verify_lines.append(f"custom_md5(Part 5):     {info['part5_md5'].hex()}")
            verify_lines.append(f"cipher_encrypt(Part 6): {info['part6_decrypted'].hex()}")
            verify_lines.append(f"结果: {'通过 &#9989;' if info['hash2_valid'] else '失败 &#10060;'}")
            verify_lines.append("")
            verify_lines.append("ElGamal 验证数据 (条件性遗留路径)")
            verify_lines.append("-" * 40)
            verify_lines.append(f"hash_data: {len(info['elgamal_hash_data'])} 字节")
            verify_lines.append(f"签名 r 位长度: {info['elgamal_r_int'].bit_length()}")
            verify_lines.append(f"签名 s 位长度: {info['elgamal_s_int'].bit_length()}")
            verify_lines.append("注: ElGamal 仅在特定时区触发 (Bias>240 或 Bias<-600)")
            self._set_text(self.verify_text, "\n".join(verify_lines))

            # 文件结构
            struct_lines = []
            struct_lines.append("=" * 60)
            struct_lines.append("wincmd.key 文件结构 (0x400 = 1024 字节)")
            struct_lines.append("=" * 60)
            parts = [
                ("Part 0", PART0_OFF, PART0_LEN, "保留区 (旧版本占位, 未使用)"),
                ("Part 1", PART1_OFF, PART1_LEN, "LUC 签名 S (使用前 0x68 字节, 小端)"),
                ("Part 2", PART2_OFF, PART2_LEN, "ElGamal 验证数据 1 (加密 seed=0x5862CF)"),
                ("Part 3", PART3_OFF, PART3_LEN, "ElGamal 验证数据 2 (加密 seed=0x1E0F5)"),
                ("Part 4", PART4_OFF, PART4_LEN, "保留区 (程序不访问)"),
                ("Part 5", PART5_OFF, PART5_LEN, "第二次哈希输入 (固定版权字符串)"),
                ("Part 6", PART6_OFF, PART6_LEN, "加密后 MD5 哈希值"),
            ]
            for name, off, length, desc in parts:
                data_part = info[name.lower().replace(" ", "")]
                hex_preview = data_part[:32].hex()
                if len(data_part) > 32:
                    hex_preview += " ..."
                struct_lines.append(f"\n[{name}] 偏移 0x{off:03X} ~ 0x{off+length:03X} ({length} 字节)")
                struct_lines.append(f"  说明: {desc}")
                struct_lines.append(f"  Hex:  {hex_preview}")
            self._set_text(self.struct_text, "\n".join(struct_lines))

            # 黑名单
            bl_lines = []
            bl_lines.append("=" * 60)
            bl_lines.append("黑名单检查 (4 个列表均为黑名单)")
            bl_lines.append("=" * 60)
            if info["blacklist_check"]:
                bl = info["blacklist_check"]
                bl_lines.append(f"盗版列表1 (g_PiratedKeysList1, {PIRATED_KEYS_LIST1_COUNT}条): {'匹配! &#10060;' if bl['in_pirated_list1'] else '未匹配 &#9989;'}")
                bl_lines.append(f"盗版列表2 (g_PiratedKeysList2, {PIRATED_KEYS_LIST2_COUNT}条): {'匹配! &#10060;' if bl['in_pirated_list2'] else '未匹配 &#9989;'}")
                bl_lines.append(f"预注册列表 (g_ValidMasterKeys, {VALID_MASTER_KEYS_COUNT}条): {'匹配! &#10060;' if bl['in_valid_master_keys'] else '未匹配 &#9989;'}")
                bl_lines.append(f"吊销黑名单 (g_RevokedKeysBlacklist, {REVOKED_KEYS_BLACKLIST_COUNT}条): {'匹配! &#10060;' if bl['in_revoked_blacklist'] else '未匹配 &#9989;'}")
                bl_lines.append(f"特殊值检查 (user_count==0xFFFF || license_num==0xFFFFFF): {'是 → 无效! &#10060;' if bl['special_invalid'] else '否 &#9989;'}")
                bl_lines.append("")
                bl_lines.append(f"注册状态: {bl['state_str']}")
                bl_lines.append(f"许可证是否无效: {'是 &#10060;' if bl['license_invalid'] else '否 &#9989;'}")
            else:
                bl_lines.append(f"未找到 {BINARY},无法检查黑名单")
            self._set_text(self.blacklist_text, "\n".join(bl_lines))

            self.status.config(text="验证完成")

        except Exception as e:
            import traceback
            self.status.config(text="验证失败")
            messagebox.showerror("验证错误", f"解析失败:\n{str(e)}\n\n{traceback.format_exc()}")


def main():
    app = WinCmdKeyValidator()
    app.mainloop()


if __name__ == "__main__":
    main()

1.png (35.84 KB, 下载次数: 1)

1.png

2.png (43.83 KB, 下载次数: 0)

2.png

3.png (52.25 KB, 下载次数: 0)

3.png

4.png (35.84 KB, 下载次数: 0)

4.png

免费评分

参与人数 4吾爱币 +4 热心值 +4 收起 理由
竹轩 + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
laozhang4201 + 1 + 1 热心回复!
xj3121 + 1 + 1 --------
shiyue2925 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
Turbo86 发表于 2026-9-4 19:33
先收藏下来
3#
忧郁之子 发表于 2026-9-4 19:37
这个是个好东西,收藏一下,用的时候直接Python运行
4#
竹轩 发表于 2026-9-5 09:16
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-9-5 10:47

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表