注:本文仅用于技术研究与探讨,请自行在下载24小时后删除

最近需要用到表格处理 实在是不太懂 想起来以前用到的方方格子 挺方便的

这就去下载了最新离线全功能版本

又想起来当时用了一个大神的注册机 翻出来发现注册后提示注册时间超限 这就很难受了 这就研究一下

托入ExeinfoPe 看看

有混淆 不慌 用工具去掉混淆后就可以用DnSpy分析了

啪的一下就出来了 发现注册时间锁死在9999-12-31

但是不太懂C# 把大概的核心代码整理 加ai辅助 直接写出网页版


<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>方方格子离线登录注册机</title>
    <style>
        * {
            box-sizing: border-box;
            font-family: 'Microsoft YaHei', sans-serif;
        }
        body {
            background: linear-gradient(135deg, #1e5799 0%, #207cca 51%, #2989d8 100%);
            color: #333;
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            margin: 0;
            padding: 20px;
        }
        .container {
            background: white;
            border-radius: 10px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            width: 100%;
            max-width: 500px;
            padding: 25px;
            position: relative;
            overflow: hidden;
        }
        .container::before {
            content: "";
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            height: 5px;
            background: linear-gradient(90deg, #ff7e00, #ffcc00, #ff7e00);
        }
        h1 {
            text-align: center;
            color: #1e5799;
            margin-top: 0;
            margin-bottom: 25px;
            font-size: 24px;
            position: relative;
        }
        .form-group {
            margin-bottom: 20px;
        }
        label {
            display: block;
            margin-bottom: 8px;
            font-weight: bold;
            color: #1e5799;
        }
        input[type="text"] {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 16px;
            transition: border-color 0.3s;
        }
        input[type="text"]:focus {
            border-color: #1e5799;
            outline: none;
            box-shadow: 0 0 5px rgba(30, 87, 153, 0.3);
        }
        .btn-group {
            display: flex;
            gap: 15px;
            margin-top: 10px;
        }
        button {
            flex: 1;
            padding: 12px;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s;
        }
        .btn-generate {
            background: linear-gradient(to bottom, #1e5799, #207cca);
            color: white;
        }
        .btn-generate:hover {
            background: linear-gradient(to bottom, #15467a, #1a65a8);
            transform: translateY(-2px);
            box-shadow: 0 5px 10px rgba(30, 87, 153, 0.3);
        }
        .btn-copy {
            background: linear-gradient(to bottom, #27ae60, #2ecc71);
            color: white;
        }
        .btn-copy:hover {
            background: linear-gradient(to bottom, #219653, #27ad5f);
            transform: translateY(-2px);
            box-shadow: 0 5px 10px rgba(46, 204, 113, 0.3);
        }
        .info {
            background: #e3f2fd;
            border-left: 4px solid #1e5799;
            padding: 15px;
            border-radius: 0 5px 5px 0;
            margin-top: 20px;
            font-size: 14px;
        }
        .info h3 {
            margin-top: 0;
            color: #1e5799;
        }
        .copy-notification {
            position: fixed;
            top: 20px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(46, 204, 113, 0.9);
            color: white;
            padding: 10px 20px;
            border-radius: 5px;
            font-weight: bold;
            opacity: 0;
            transition: opacity 0.3s;
            z-index: 1000;
        }
        @media (max-width: 480px) {
            .btn-group {
                flex-direction: column;
            }
            .container {
                padding: 15px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>方方格子离线登录注册机</h1>
        
        <div class="form-group">
            <label for="computerNo">本机编号:</label>
            <input type="text" id="computerNo" placeholder="请输入本机编号(如CN1A3F)">
        </div>
        
        <div class="form-group">
            <label for="expiryDate">到期日期:</label>
            <input type="date" id="expiryDate">
        </div>
        
        <div class="form-group">
            <label for="license">授权码:</label>
            <input type="text" id="license" readonly>
        </div>
        
        <div class="btn-group">
            <button class="btn-generate" onclick="generateLicense()">生成授权码</button>
            <button class="btn-copy" onclick="copyLicense()">复制授权码</button>
        </div>
        
        <div class="info">
            <h3>使用说明:</h3>
            <p>1. 输入您的本机编号(通常以CN开头)</p>
            <p>2. 选择到期日期(默认为一年后)</p>
            <p>3. 点击"生成授权码"按钮获取授权码</p>
            <p>4. 点击"复制授权码"按钮复制生成的授权码</p>
            <p>5. 在方方格子软件中使用此授权码进行离线登录</p>
        </div>
    </div>
    
    <div class="copy-notification" id="copyNotification">授权码已复制到剪贴板!</div>
    
    <script>
        // 页面加载时设置日期选择器默认为当前日期加一年
        window.onload = function() {
            const today = new Date();
            const nextYear = new Date(today);
            nextYear.setFullYear(today.getFullYear() + 1);
            
            const dateControl = document.getElementById('expiryDate');
            dateControl.valueAsDate = nextYear;
        };
        
        // 生成授权码
        function generateLicense() {
            const computerNo = document.getElementById('computerNo').value.trim();
            if (!computerNo) {
                alert('请输入本机编号');
                return;
            }
            
            // 按照原C#代码逻辑:GetEncodeComputerName(text)
            // 注意:这里传入的是完整的computerNo,而不是去掉CN的部分
            const encodeComputerName = getEncodeComputerName(computerNo);
            
            // DateTime.MaxValue.ToString("yyyyMMdd") = "99991231"
            // 但是为了与用户选择的日期兼容,我们使用选择的日期
            const dateControl = document.getElementById('expiryDate');
            let expiryDate;
            if (dateControl.value) {
                const selectedDate = new Date(dateControl.value);
                const year = selectedDate.getFullYear();
                const month = String(selectedDate.getMonth() + 1).padStart(2, '0');
                const day = String(selectedDate.getDate()).padStart(2, '0');
                expiryDate = `${year}${month}${day}`;
            } else {
                expiryDate = "99991231"; // 默认使用DateTime.MaxValue
            }
            
            // Guid.NewGuid().ToString("N").Substring(0, 8)
            const randomString = generateGuidLikeString(8);
            
            // MD5(encodeComputerName + randomString + expiryDate)
            const toHash = encodeComputerName + randomString + expiryDate;
            const hashResult = md5(toHash);
            
            let licensePart = '';
            if (hashResult.length >= 8) {
                licensePart = hashResult.substring(hashResult.length - 8);
            } else {
                licensePart = hashResult.padStart(8, '0');
            }
            
            const part1 = licensePart.substring(0, 2);
            const part2 = licensePart.substring(2, 8); // 修正:应该是6个字符
            
            // 按照原C#代码的字符串拼接格式
            const license = `${randomString}-${expiryDate}-${encodeComputerName}${part1}-${part2}`;
            document.getElementById('license').value = license;
        }
        
        // 获取编码后的计算机名(根据原C#代码实现)
        function getEncodeComputerName(computerName) {
            // 完全按照原C#代码逻辑实现
            // 参考自 public string GetEncodeComputerName(string computerName) 方法
            const text = md5(computerName);
            let result;
            if (text.length >= 4) {
                result = text.substring(text.length - 4, text.length);
            } else {
                result = text.padStart(4, 'a');
            }
            return result.toUpperCase(); // 确保结果是大写
        }
        
        // 复制授权码到剪贴板
        function copyLicense() {
            const license = document.getElementById('license');
            if (!license.value) {
                alert('请先生成授权码');
                return;
            }
            
            license.select();
            document.execCommand('copy');
            
            // 显示复制成功通知
            const notification = document.getElementById('copyNotification');
            notification.style.opacity = '1';
            setTimeout(() => {
                notification.style.opacity = '0';
            }, 2000);
        }
        
        // 生成随机字符串
        function generateRandomString(length) {
            const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
            let result = '';
            for (let i = 0; i < length; i++) {
                result += chars.charAt(Math.floor(Math.random() * chars.length));
            }
            return result;
        }
        
        // 生成类似GUID的随机字符串(更接近C#的Guid.NewGuid().ToString("N")实现)
        function generateGuidLikeString(length) {
            const hexChars = '0123456789abcdef';
            let result = '';
            for (let i = 0; i < length; i++) {
                result += hexChars.charAt(Math.floor(Math.random() * hexChars.length));
            }
            return result;
        }
        
        // MD5 哈希函数实现
        function md5(input) {
            function rotateLeft(value, shift) {
                return (value << shift) | (value >>> (32 - shift));
            }
            
            let h0 = 0x67452301;
            let h1 = 0xEFCDAB89;
            let h2 = 0x98BADCFE;
            let h3 = 0x10325476;
            
            // 预处理:填充输入
            const msgLength = input.length;
            const totalLength = msgLength + 9;
            const blockCount = Math.ceil(totalLength / 64);
            const paddedMessage = new Uint8Array(blockCount * 64);
            
            // 复制原始消息
            for (let i = 0; i < msgLength; i++) {
                paddedMessage[i] = input.charCodeAt(i);
            }
            
            // 添加填充位
            paddedMessage[msgLength] = 0x80;
            
            // 添加消息长度(小端序)
            const bitLength = msgLength * 8;
            paddedMessage[blockCount * 64 - 8] = bitLength & 0xff;
            paddedMessage[blockCount * 64 - 7] = (bitLength >>> 8) & 0xff;
            paddedMessage[blockCount * 64 - 6] = (bitLength >>> 16) & 0xff;
            paddedMessage[blockCount * 64 - 5] = (bitLength >>> 24) & 0xff;
            
            // 处理每个512位块
            for (let block = 0; block < blockCount; block++) {
                const words = new Array(16);
                const start = block * 64;
                
                // 将块分成16个32位字
                for (let i = 0; i < 16; i++) {
                    words[i] = 
                        (paddedMessage[start + i * 4 + 0]      ) |
                        (paddedMessage[start + i * 4 + 1] << 8 ) |
                        (paddedMessage[start + i * 4 + 2] << 16) |
                        (paddedMessage[start + i * 4 + 3] << 24);
                }
                
                let a = h0;
                let b = h1;
                let c = h2;
                let d = h3;
                
                // 主循环
                for (let i = 0; i < 64; i++) {
                    let f, g;
                    
                    if (i < 16) {
                        f = (b & c) | ((~b) & d);
                        g = i;
                    } else if (i < 32) {
                        f = (d & b) | ((~d) & c);
                        g = (5 * i + 1) % 16;
                    } else if (i < 48) {
                        f = b ^ c ^ d;
                        g = (3 * i + 5) % 16;
                    } else {
                        f = c ^ (b | (~d));
                        g = (7 * i) % 16;
                    }
                    
                    const temp = d;
                    d = c;
                    c = b;
                    b = b + rotateLeft((a + f + k[i] + words[g]) | 0, r[i]);
                    a = temp;
                }
                
                h0 = (h0 + a) | 0;
                h1 = (h1 + b) | 0;
                h2 = (h2 + c) | 0;
                h3 = (h3 + d) | 0;
            }
            
            // 生成最终哈希值
            const result = new Uint8Array(16);
            function writeInt32(value, offset) {
                result[offset] = value & 0xff;
                result[offset + 1] = (value >>> 8) & 0xff;
                result[offset + 2] = (value >>> 16) & 0xff;
                result[offset + 3] = (value >>> 24) & 0xff;
            }
            
            writeInt32(h0, 0);
            writeInt32(h1, 4);
            writeInt32(h2, 8);
            writeInt32(h3, 12);
            
            // 转换为十六进制字符串
            return Array.from(result).map(b => 
                b.toString(16).padStart(2, '0')).join('').toUpperCase();
        }
        
        // MD5常量
        const k = [
            0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
            0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
            0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
            0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
            0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
            0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
            0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
            0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
            0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
            0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
            0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
            0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
            0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
            0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
            0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
            0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
        ];
        
        const r = [
            7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
            5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,
            4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
            6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
        ];
    </script>
</body>
</html>

然后发现再去5.1.0.0版本注册发现提示注册时间超限制

然后我试了最大注册年限是365天 不能超过哪怕一天当我设置为365天后 提示

所以新版改注册算法了

后来我发现 可以先安装可以注册版本 注册后 最新版也会是注册状态 再把旧版本卸载就行

注意修改安装地址 不能和最新版重复 我这里方便演示加了个数字1

安装后 提示安装环境点击否

然后打开安装目录C:\Program Files (x86)\方方格子1\Tools\ExcelSummary 运行里面的Summary.exe

将上面的代码保存为html文件打开

注册成功后关闭窗口 然后再回到C:\Program Files (x86)\方方格子1\ 运行unins000.exe把旧版删除了

然后重新安装最新版本就行 会带一年授权 就可以继续愉快玩耍了

所以最终方案:

先安装方方格子 Excel工具箱 V3.6.8.2 Setup.exe

在提示安装VSTO环境的时候点否

然后打开打开安装目录

默认:C:\Program Files (x86)\方方格子\Tools\ExcelSummary\Summary.exe(以你安装目录为准)

点击系统设置-详细信息-离线登录-用html页面计算注册码-确定绑定

注册后运行C:\Program Files (x86)\方方格子\unins000.exe将该旧版本删除

再全新安装最新版本(这次记得安装VSTO环境哦)

会直接继承这一年的授权 安装新版后不要在账号里面注销 不然要重新操作一遍了

PS:就是一年后又要重新操作一次 但是没有任何外部暴力破解 安全性高

下载链接:https://www.123912.com/s/Qgadjv-VBdI