crc16求未知多项式ploy和crc初始值init

背景:

破解老旧电气设备通讯,使用串口助手截获数据(Hex)并分析,假定各字节作用——校验码两字节16位,尝试常见 crc16 计算校验码均失败。

目标:

根据已有数据,通过编程计算出未知多项式 ploy 和crc初始值 init 。

编程思路:

crc16 的 ploy 和 init 均为16位(0~0xFFFF),两层for循环尝试所有 poly 和 init 的组合可能(65536*65536),使poly和init能代入已有数据。

编程环境:

计算机:联想拯救者Y7000 2019 PG0 (CPU:英特尔 Core i5-9300H @ 2.40GHz 四核,内存:8 GB ,记忆科技 DDR4 2666MHz )
IDE:Visual Studio 2019
编程语言:C#
其它:.Net 5.0 + Windows 应用程序

代码效果(尝试所有 poly 和 init 的组合可能所花费的时间):

1.采用 crc 直接计算法,60+分钟。
2.采用 crc 全字节查表法,< 4分钟。

代码( crc 直接计算法):

#region crc16求未知多项式ploy和crc初始值init
    /// <summary>
    /// crc16求未知多项式ploy和crc初始值init
    /// </summary>
    public class CRC16_Get_Polynomial_InitialValue
    {
        static CRC16_Get_Polynomial_InitialValue()
        {

        }

        /// <summary>
        /// crc16求未知多项式ploy和crc初始值init
        /// </summary>
        /// <returns></returns>
        public  ushort CRC16_get_Polynomial_InitialValue()
        {
            //已知参与计算校验码的数据(除帧头、固定字段、校验位、帧尾以外的数据)
            byte[] date1 = { 0x01, 0x04, 0x0C, 0x01, 0x00, 0x32, 0x00 };
            byte[] date2 = { 0x01, 0x04, 0x0C, 0x01, 0x00, 0x31, 0x00 };
            byte[] date3 = { 0x01, 0x04, 0x0A, 0x01, 0x00, 0x0C, 0x00 };
            //已知数据的校验码
            ushort crc1 = 0x59D8;
            ushort crc2 = 0x31F2;
            ushort crc3 = 0x73CF;
            //假设计算得到校验码后交换高低字节数据,还原校验码未交换高低字节前的数据
            ushort crc11 = (ushort)(crc1 << 8 | crc1 >> 8);
            ushort crc22 = (ushort)(crc2 << 8 | crc2 >> 8);
            ushort crc33 = (ushort)(crc3 << 8 | crc3 >> 8);

            
            bool get = false;//用于得到poly和init后终止for循环
            //poly为多项式, 使用for循环来尝试不同的poly值
            for (int poly = 0; poly <= 0xFFFF; poly++)
            {
                //校验得出的poly是否唯一
                //if (poly == 0x8408)
                //{
                //    continue;
                //}
                //init为crc初始值,使用for循环来尝试不同的init值 
                for (int init = 0; init <= 0xFFFF; init++)
                {
                    ushort cRc_16 = Get_cRc_16(date1, init, poly);
                    if (cRc_16 == crc1)
                    {
                        cRc_16 = Get_cRc_16(date2, init, poly);
                        if (cRc_16 == crc2)
                        {
                            cRc_16 = Get_cRc_16(date3, init, poly);
                            if (cRc_16 == crc3)
                            {
                                MessageBox.Show("poly为" + poly.ToString("X2") + ",init为" + init.ToString("X2") + "。");
                                get = true;
                                break;//终止for循环
                            }
                            
                        }
                    }
                    else if (cRc_16 == crc11)
                    {
                        cRc_16 = Get_cRc_16(date2, init, poly);
                        if (cRc_16 == crc22)
                        {
                            cRc_16 = Get_cRc_16(date3, init, poly);
                            if (cRc_16 == crc33)
                            {
                            MessageBox.Show("poly为" + poly.ToString("X2") + ",init为" + init.ToString("X2") + ",得到的校验码需要高低字节互换。");
                            get = true;
                            break;
                            }
                        }
                    }

                }
                if (get)
                {
                    break;
                }
            }
            if (!get)
            {
                MessageBox.Show("计算失败,请检查程序逻辑和操作的数据");
            }
            return 0;
        }

        /// <summary>
        /// crc16直接计算法
        /// </summary>
        /// <param name="date"></param>
        /// <param name="init"></param>
        /// <param name="poly"></param>
        /// <returns></returns>
        private static ushort Get_cRc_16(byte[] date, int init, int poly)
        {
            ushort cRc_16 = (ushort)init;
            for (int i = 0; i < date.Length; i++) // 遍历字节数组date
            {
                cRc_16 = (ushort)(cRc_16 ^ date[i]); // 对cRc_16进行异或操作
                for (int j = 0; j < 8; j++)
                {
                    if ((cRc_16 & 0x0001) != 0) // 如果cRc_16的最低位为1
                    {
                        cRc_16 = (ushort)((cRc_16 >> 1) ^ poly); // 右移一位并与poly进行异或操作
                    }
                    else // 如果cRc_16的最低位为0
                    {
                        cRc_16 = (ushort)(cRc_16 >> 1); // 直接右移一位
                    }
                }
            }
            return cRc_16;
        }
        #endregion

代码(crc 全字节查表法):

    #region crc16求未知多项式ploy和crc初始值init
    /// <summary>
    /// crc16求未知多项式ploy和crc初始值init
    /// </summary>
    public class CRC16_Get_Polynomial_InitialValue
    {
        static CRC16_Get_Polynomial_InitialValue()
        {

        }

        /// <summary>
        /// crc16求未知多项式ploy和crc初始值init
        /// </summary>
        /// <returns></returns>
        public  ushort CRC16_get_Polynomial_InitialValue()
        {
            //已知参与计算校验码的数据(除帧头、固定字段、校验位、帧尾以外的数据)
            byte[] date1 = { 0x01, 0x04, 0x0C, 0x01, 0x00, 0x32, 0x00 };
            byte[] date2 = { 0x01, 0x04, 0x0C, 0x01, 0x00, 0x31, 0x00 };
            byte[] date3 = { 0x01, 0x04, 0x0A, 0x01, 0x00, 0x0C, 0x00 };
            //已知数据的校验码
            ushort crc1 = 0x59D8;
            ushort crc2 = 0x31F2;
            ushort crc3 = 0x73CF;
            //假设计算得到校验码后交换高低字节数据,还原校验码未交换高低字节前的数据
            ushort crc11 = (ushort)(crc1 << 8 | crc1 >> 8);
            ushort crc22 = (ushort)(crc2 << 8 | crc2 >> 8);
            ushort crc33 = (ushort)(crc3 << 8 | crc3 >> 8);

            
            bool get = false;//用于得到poly和init后终止for循环
            //poly为多项式, 使用for循环来尝试不同的poly值
            for (int poly = 0; poly <= 0xFFFF; poly++)
            {
                //校验得出的poly是否唯一
                //if (poly == 0x8408)
                //{
                //    continue;
                //}
                GenerateCRCTable(poly);
                //init为crc初始值,使用for循环来尝试不同的init值 
                for (int init = 0; init <= 0xFFFF; init++)
                {
                    ushort cRc_16 = GetCRC16(date1,init);
                    if (cRc_16 == crc1)
                    {
                        cRc_16 = GetCRC16(date2, init);
                        if (cRc_16 == crc2)
                        {
                            cRc_16 = GetCRC16(date3, init);
                            if (cRc_16 == crc3)
                            {
                                MessageBox.Show("poly为" + poly.ToString("X2") + ",init为" + init.ToString("X2") + "。");
                                get = true;
                                break;//终止for循环
                            }
                            
                        }
                    }
                    else if (cRc_16 == crc11)
                    {
                        cRc_16 = GetCRC16(date2, init);
                        if (cRc_16 == crc22)
                        {
                            cRc_16 = GetCRC16(date3, init);
                            if (cRc_16 == crc33)
                            {
                            MessageBox.Show("poly为" + poly.ToString("X2") + ",init为" + init.ToString("X2") + ",得到的校验码需要高低字节互换。");
                            get = true;
                            break;
                            }
                        }
                    }

                }
                if (get)
                {
                    break;
                }
            }
            if (!get)
            {
                MessageBox.Show("计算失败,请检查程序逻辑和操作的数据");
            }
            return 0;
        }

        private ushort[] _crcTable = new ushort[256];
        /// <summary>
        /// 得到校验码表
        /// </summary>
        /// <param name="poly"></param>
        private void GenerateCRCTable(int poly)
        {

            // 使用两个嵌套的循环来计算每个可能的字节值的CRC-16校验码
            for (int i = 0; i < 256; i++)
            {
                //crc赋值从0到256(00到FF)
                ushort crc = (ushort)i;
                //MessageBox.Show(string.Join("", crc));
                for (int j = 0; j < 8; j++)
                {
                    // 如果当前字节的最高位为1,则将CRC右移一位并与多项式0xA001进行异或操作
                    if ((crc & 1) != 0)
                    {
                        crc = (ushort)((crc >> 1) ^ poly);
                    }
                    // 否则,将CRC右移一位
                    else
                    {
                        crc >>= 1;
                    }

                }
                // 将计算出的CRC-16校验码存储在_crcTable数组中
                _crcTable[i] = crc;

            }
        }
        
        /// <summary>
        /// 全字节查表法
        /// </summary>
        /// <param name="data"></param>
        /// <param name="init"></param>
        /// <returns></returns>
        public ushort GetCRC16(byte[] data, int init)
        {
            ushort cRc_16 = (ushort)init;
            // 遍历输入数据的每一个字节
            foreach (byte b in data)
            {
                // 使用预先生成的_cRc_16Table数组来计算校验码,并将结果与cRc_16右移8位后的结果进行异或操作
                cRc_16 = (ushort)(_crcTable[(cRc_16 ^ b) & 0xFF] ^ (cRc_16 >> 8));
            }
            //cRc_16高低字节互换
            //cRc_16 = (ushort)((cRc_16 << 8) | cRc_16 >> 8);
            // 返回计算出的cRc_16-16校验码
            return cRc_16;
        }
        #endregion

参考:

1.CRC(循环冗余校验)查表算法的代码实现

2.【基础知识】CRC(循环冗余校验)直接计算和查表法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值