精华内容
下载资源
问答
  • CRC原理

    2011-02-25 15:59:54
    CRC.......................................................................................
  • 我学习CRC32、CRC16、CRC 原理和算法的总结(与WINRAR 结果一致),里面详细描述了CRC原理,应用,及相应推导过程,是CRC讲得最全的,从入门到高阶及C语言写的例程都有!~~
  • CRC原理和C语言实现CRC

    2020-08-25 15:02:16
    一、CRC原理 CRC是对传输的数据进行一个差错校验的,但是不对数据进行纠错,CRC检测不通过的时候丢弃数据 CRC冗余校验 首先先明确几个点: 多项式的被除数M,也就是信息码(数据) 多项式的除数G 得到的结果冗余校验...
    〇、参考文章
    1. 最通俗的CRC校验原理剖析
    2. 循环冗余校验(CRC)算法入门引导
    3. CRC的基本原理详解
    4. CRC编码及C++实现
    5. CRC原理简介
    一、CRC原理

    CRC是对传输的数据进行一个差错校验的,但是不对数据进行纠错,CRC检测不通过的时候丢弃数据

    CRC冗余校验

    首先先明确几个点:

    • 多项式的被除数M,也就是信息码(数据)
    • 多项式的除数G
    • 得到的结果冗余校验码R
    • 以及冗余校验码的宽度W

    【注意】
    G得到的二进制长度是比W1位的,如计算CRC4,对应的宽度W4,但是生成的多项式虽然说是任意的,但是x⁴这个是必然存在的
    假设生成多项式x⁴+x³+x⁰,也就是说G对应的二进制是11001,这样,二进制对应的长度就比W1位,因为第1位必然是1,所以会把第1位忽略掉,对应的异或的值为1001,为什么忽略最高位的1,下面会解释


    下面看一下原理
    CRC算法的是以GF(2)(2元素伽罗瓦域)多项式算术为数学基础的,注意,这是一种新的运算法则
    法则规定,加减不考虑进位,所以加减是一致的,式子中不出现减号,以加号代替,因此可以用二进制的异或来运算其加法(在运算异或的时候就是加法)

    P1 = x^3  + x^2 + 1,P2 = x^3  + x^1 + 1,P1 + P2为:
    
    	x^3 + x^2     + 1									1101
    +	x^3       + x  + 1						         ^  1011
    ------------------------------      >>      ------------------------------
              x^2 + x                                       0110
    

    了解一下异或的一些性质:

    1. a ⊕ a = 0
    2. a ⊕ b = b ⊕ a    						//  异或运算满足交换律
    3. a ⊕ b ⊕ c = a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c; //  异或运算满足结合律
    4. d = a ⊕ b ⊕ c 可以推出 a = d ⊕ b ⊕ c.
    5. a ⊕ b ⊕ a = b.
    

    发送和接收的双方确定一个除数G,发送的数据对除数进行一个模2运算,将得到的余数添加到要发送的数据后面去(不是相加,是添加)
    接收方接收到数据之后对同样的余数进行模2运算,得到的余数应该是0

    假设发送的数据是10110011,双方确定的除数是11001(没有忽略最高位),确定的冗余码是4位,需要在信息码的后面添加40

    这里的模2运算就是数据后面添加冗余位数再与除数的异或运算,在最后的运算中,落下来到被除数上面的肯定是0,因为是后面添加上去的,就是图中4个附加上去的0,这就使得最后一次的运算总是被除数(11000)小于除数(11001),而他们的差异也就是余数了(0110),不够确定的冗余位数的需要补齐

    在这里插入图片描述
    得到冗余码之后,添加到数据的后面发送给接收方,接收方获得数据后,对同样的除数进行模2运算,得到的结果应该为0,为什么是0
    因为上面说了最后得出余数的那一次运算是被除数和除数之间的差异,在被除数添加上这个差异后,就不会再有余数了,故而余数为0,比如11000中,110是原来被除数落下来的,添加上0100后就是1100100,与除数(11001)做模2运算得出来的结果是0


    二、CRC检验C语言

    中间的循环代码还是比较好理解的,

    for (i = 0; i < 8; i++)             // 一个字节8位,一位一位的去异或
       {
           if (crc & 0x8000)            // 判断CRC的首位,如果是1的活,那么左移一位之后,其首位和除数被忽略的那个最高位的1异或结果位0,也可以忽略
               crc = (crc << 1) ^ POLY;	// 左移后进行异或
           else                         // 首位为0,比如异或之后结果为0,应该选首位为1的位开始继续异或,直接跳过0位
               crc <<= 1;               
       }                             
    
    	 -------------------
    11001|10110011
    	 11001		(为什么首位1在外面呢,应为代码里面的除数是没有首位的,即1001)
    	 -------------------
    	 011110110 (可以看到两者都是1,异或结果必然是0,可以直接忽略,接下来首位还是1,先移位)
    	 111101100
    	 11001
    	 -------------------
    	 001111100(忽略前面的一个0,剩下的是01111100,继续异或必然不能是下面的情况)
    	  011111000(所以首是0,跳过)(else crc<<1)
    	  1001
    

    关于下面这句话的理解,先把数据左移8位,因为下面一位一位的运算时还要移动8位,一共就是16CRC宽度,然后异或上crc,可以理解为crc时上次的余数,然后加上这次的数据,一起继续异或

    crc = crc ^ (*addr++ << 8);     	// 先是把第一个字节的数据左移8位
    
    如数据01,02连接在一起是
    0000 0001 0000 0010, 相当于0000 0001 0000 0000 + 0000 0000 0000 0010,即(01<<8)^(02)
    (01<<8)^(02)^POLY ==> (01<<8)^POLY^(02)
    (01<<8)^POLY相当于一次的外循环
    

    那么结合起来的总代码就是下面的这个

    #define POLY        0x1021
    /**
     *	@param:	addr:	存放数据的数组地址
     *			lenth:	数组长度
     *			crc:	crc初始化的值
     */
    uint16_t crc16(unsigned char *addr, int lenth, uint16_t crc)
    {
        int i;
        for (; lenth> 0; lenth--)           	// 数组有多少字节进行多少次计算
        {
            crc = crc ^ (*addr++ << 8);     	
            for (i = 0; i < 8; i++)            
            {
                if (crc & 0x8000)           
                    crc = (crc << 1) ^ POLY;   
                else                          
                    crc <<= 1;                 
            }                            
            crc &= 0xFFFF;                 // 确保出来的CRC是16位,多余的清零
        }                               
        return(crc);                   
    }
    

    2020.8.25
    展开全文
  • crc原理与应用

    2012-05-19 20:20:14
    crc原理与应用及奇偶效验码原理与应用
  • 网络纠错CRC原理

    2011-12-15 20:01:33
    网络纠错CRC原理 同步光纤网 SONET 和同步数字系列 SDH 循环冗余检验的原理
  • CRC原理及计算方法

    2012-09-12 17:06:08
    CRC原理 如何创建CRC验证码。 CRC-7的详细讲解。
  • 循环冗余检验CRC原理

    2018-11-20 16:47:09
    循环冗余检验CRC原理

    分享一下我老师大神的人工智能教程吧。零基础,通俗易懂!风趣幽默!http://www.captainbed.net/

    也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                         

    为什么引入CRC

    现实的通信链路都不会是理想的。这就是说,比特在传输的过程中可能会产生差错:1可能会变成0,0可能会变成1,这就叫做比特差错。在一段是时间内,传输错误的比特占所传输比特总数的比率成为误码率BER(Bit Error Rate)。误码率与信噪比有很大的关系,在实际通信中不可能使误码率下降到零。
    因此,为了保证数据传输的可靠性,在计算机网络传输数据时,必须采用各种差错检测措施。
    目前在数据链路层广泛使用了循环冗余检测CRC的检测技术

    CRC的原理

    CRC运算实际上就是在数据长为k的后面添加供差错检测用的n位冗余码,然后构成帧k+n位发送出去。

    首先来介绍几个概念
    (1)模2运算:实际上是按位异或运算,即相同为0,相异为1,也就是不考虑进位、借位的二进制加减运算。如:1111+1010 = 0101
    (2)FCS:其实就是冗余码,帧检验序列(Frame Check Sequence)
    (3)生成多项式:其实就是除数,比如下面将要用到的除数p = 1101

    这里写图片描述

    计算n位冗余码

    现假定待传输的数据M = 101001(k = 6),除数p = 1101   (n = 3)比n多一位
    这n位冗余码可以用下面的方法得出。
    (1)用二进制的模2运算进行(2^n)乘M的运算,相当于在M后面添加n个0
        即M后面添加3个0
    (2)现在得到M = 101001000(k+n = 9)位的数除以除数p(n = 3)位,
    得到商是Q(不关心),余数R =001(n位)R就是冗余码FCS

    现在加上FCS后发送的帧是101001001
    这里写图片描述

    在接收端把接收到的数据M = 101001001以帧为单位进行CRC检验:把收到的每一个帧都除以相同的除数p(模2运算),然后检查得到的余数R。
    如果在传输过程中没有差错,那么经过检验后得到余数R肯定是0。
    (读者可以自己检验下,被除数现在是M = 101001001,除数P= 1101,看余数是否为0)
    总之,在接收端对接收到的每一个帧经过CRC检验后,有两种情况:
    (1)余数R = 0,则判断这个帧没有问题,就接受
    (2)余数R != 0,则判断这个帧有差错,就丢弃。

    总结一下:

    在数据链路层若仅仅使用CRC差错检验技术,则只能做到对帧的无差错接收。

               

    给我老师的人工智能教程打call!http://www.captainbed.net/

    这里写图片描述
    展开全文
  • 不要跑,CRC没这么难!(简单易懂的CRC原理阐述)

    万次阅读 多人点赞 2019-02-02 19:22:38
    (简单易懂的CRC原理阐述) 网上大多的教材都是面向大佬的很多细节原理都没有讲清楚,对于我们这些新萌菜鸟们实在太不友好了。于是我写一篇相对轻松易懂的博客,希望能对学习CRC的朋友们有所帮助吧! 什么是CRC?...

    不要跑,CRC没这么难!(简单易懂的CRC原理阐述)

    网上大多的教材都是面向大佬的很多细节原理都没有讲清楚,对于我们这些新萌菜鸟们实在太不友好了。于是我写一篇相对轻松易懂的博客,希望能对学习CRC的朋友们有所帮助吧!

    什么是CRC???

    你说小学3年级的小明同学不知好歹喜欢村长女儿王大麻子,于是羞涩的他想到写一封情书已表心意。正所谓闺女似情人,爱女心切的村长凡是信件统统要过他之手。如果这份情书被她爸稍加“几笔”岂不悲剧了?

    奇偶验证

             如何验证情书是否被动过手脚(验证数据是否损坏),介于王大麻子数学不行,数数还行。作为数学课代表的小明同学立刻想到一个好主意:将所有的文字转化二进制,只要数一数1的数量是奇数还是偶数不就可以了吗!

             比如我要传递M这个字符那么他的ASCII的值为0100 1101(M),就是数据末尾加上一个1或0,使其整个数据的1的数量可以凑齐奇数或偶数。

             如果规定信件所有1的个数为奇数的话,那么0100 1101(M)才4个1,我们就数据的末尾加上一个1凑齐奇数(5个1):0100 1101 1;如果数据中的1刚好奇数,我们就在末尾加上一个0就可(这个方法叫做奇校验),当然相反如果规定的信件所有1的个数为偶数(偶校验),刚好处理的方法也是相反。

             虽然这个方法简单到连他没有上学的弟弟都做得起(很容易通过硬件方式实现验证),但是这个的出错率是五五开啊(刚好少/多偶数个1),且永远检查不出0的错误(就算多100个零也看不出来)。

    累加和校验

             你说奇偶不行,哪我相加总该行吧?于是乎小明同学又推出第二号方案:将每一个文字(字节)的值相加来验证。

             比如传递LOVE,我们就可以得到四个数字76(L) 79(O) 86(V) 69(E),他们相加可得310,如果这个数字嫌他太大了不好写,我们可以限制他在0~255(一个字节)这个范围之内,那么我们就得到一个验证数字55 =310 – 255(最简单的办法就是减去255的倍数)。然后将数字附加数据后面,对方接受到数据执行同样的操作对比验证数字是否一致。

             虽说这个方法要比上面的方法要可靠一些,但凡事总有列外:

             一、比如数据错误刚好等于验证数字

             我们要传输的数据:76  79     86     69  310(验证数字)

             发生错误的数据:  76  78(-1)  87(+1)  69  310(验证数字还是一样的)

             二、单个字符的出错率为1/256

    CRC验证原理

             无数日夜小明同学冥思苦想,最终还剩最后三根头发之际他学会除法,头顶一凉想到一个绝佳主意:如果我将信件上的所有文字组合成一个长数字,然后用一个我和王大麻子都知道的数字(约定的多项式)去除以,将余数作为一个验证数字的话……

             假设我要传输一个字符87(W),和王大麻子约定除数为6,那么结果很明显就是87 ÷ 6 = 14……3,那么我们可以将3作为验证数字附加原始数据的末尾发送。

             但明显我们常规的借位除法大大超出了王大麻子的数学水平,于是小明同学决定不做人了发明一个二进制除法,并取名为“模二除法”。

             所谓模二除法实际形式上和我们的平常使用的除法一样的(用于二进制运算),但是唯一不一同的是关于计算当中一切的加减法统统换成“异或”(XOR),一句话概括异或运算就是:异性相吸(返回真1),同性相斥(返回假0):1 Xor 0 = 1,0 Xor 1 = 1, 1 Xor 1 = 0, 0 Xor 0 = 0。

             那么我们用上面列子来试一下模二除法(模二除法的结果不等于普通除法)

             王大麻子表示:我去!算完之后我还要核对余数?!不能再简单点吗?

             于是乎小明同学又开始没日没夜苦想,最终当最后的狼牙山“三壮士”也离他而去时,他头顶一凉想到一个绝佳主意:在信件数据的末尾(即数据低位LSB)补上我和王大麻子都知道的数字的长度-1的0(生成项长度-1)然后在被同知数字相除,得到的余数再附加原始数据的末尾上。(这里说的原始数据是指没有补零的,且余数长度一定与补零的长度一致)

             口说无凭,我们来重新用这个升级计算方法试一试。

             我们将余数10补在原始数据1010111的末尾得到了一个新数:101011110,然后我们再用110去除,神奇的事情发生了:没有余数了。(接受端只要将已修改的数据与生成项模二相除没有余数即代表数据无误)

             这便是CRC(循环冗余校验,Cyclic Redundancy Check)是一种为了检测数据是否损坏处理办法。而当中的模二除法是无借位(不向高位借位的)的性质十分重要:意味我们计算CRC只需要简单的数据位移和Xor即可(到后面你就知道了)。

             当然理论上讲CRC还是出错的可能(不过已经比程序猿能找到女朋友的几率要低了),所以选择一个好的除数就是一个非常重要的事情,关于CRC的除数有个专业的名称:生成多项式,简称生成项。如何选择生成项这是需要一定的代数编码知识(已经不是我这种咸鱼能搞懂的问题了)。好在我们可以直接用大佬计算好的生成项,不同的生成项有不同的CRC,如:CRC8、CRC16、CRC-CCITT、CRC32……。

    从数学的角度来理解CRC(如果您只是了解如何计算CRC的话可以直接跳过本节

             我们不妨尝试将二进制转化一种多项式:数字中多少位1代表的是x的多少次方,0乘以任何数都是0所以不用管。

    比如101010(42),可以转化为:x^5+x^3+x。(注意最低位0次方,任何数零次方都等于1)

             假设用x^3+x^2+x除去x+1,可以得到如下式子:(这一段完全搬运的循環冗餘校驗Wiki,写的真的好!)

             很好我们在两边再乘一个x+1:

             

             这里不难看出:得到一个商×除数+余数的式子,其中的(x^2+1)就是商,-1就是余数(我也不没懂为啥余数是负数)。我们还可以对左边式子再提取一次x,可得:

             

             我们可以理解这里提取的x是对于x^2+x+1(1011)进行补一次零变成了10110,实际上这个补零还有个说法叫做左移(要注意数据的方向高位在左边)。

             如何理解补零的性质这个很简单,我们类比十进制的补零如:1要补两次零变成100,很明显补零就是乘与10的几次方。回到二进制中就是2的几次方,而多项式中的x就可以代表2。

             通过以上的式子的整理可以得出通用公式即是:

            

              就代表的是原始数据, 代表的是数据末补n个0, 代表的是Key也就是生成项, 代表的余数也是上一节提到FCS。接收者接受到 看看是能被 整除不,可以即为正确数据。

             要注意的一点 的长度(补零长度)受限于 (目的是可以直接追加在末尾,而不影响原始数据):他们长度一致,且一定比 的长度少1。

             关于 为什么一定比 的长度少1?我个人愚见是特殊的模二除法: 的最高位一定1(不然没有意义啊),而数据处理过程中需要计算数据的最高位也是1(可以对照着除法的当中与除数对应的那个被除数的那部分数据),他们进行Xor就变成0,实际计算往往是剩下的部分( 长度-1)(在程序设计中反正都会变成0干脆都不计算首位,这就是为啥网上的CRC生成多项式的简记码都是默认舍弃首位1的原因)。

             CRC的原理实际上远比这些要复杂的多,涉及到群论这类我们这些吃瓜群众可望不可即的数理知识。以上也只是我对Wiki的搬运和理解瞎猜,希望大家能通过这个简单列子大概了解CRC的性质,如有不对之处有望大佬不惜赐教!

    直接计算法

             虽说上一节我们已经知道CRC是怎么计算的,但明显电脑可不会写除法公式(程序很难直接用这种方法)。且不说要对齐一个超长二进制数据进行逐位计算的困难不说,单单是像vbscript这种既没有几个位操作符又用起来蛋疼的语言基本上是很难实现(大佬:喵喵喵?)。不过可以转化一个思路:比如每次只取一部分数据来计算如何?

             仔细观察计算的过程,可以发现其实每一次Xor都是固定不动的生成项与其对应的数据首位“消1”。那我们就可以假想出一个与生成项长度一致的“盒子”,取出一部分的数据出来若首位是1时就进行一次Xor,遇到0则左移到1为止,左移造成的右端的空缺用0补充。而这里0希望理解为一种“存储”,它“存储” 生成项中未和数据进行计算的那一部分,按顺序先后附加被计算数据的后面,当先一部分的数据全部计算之后,实际上“盒子”中剩下都是未和数据计算的部分的“和”11011 xor 10110 = 11011 xor ( 10000 xor 00100 xor 00010)(这里实际上就是Xor的交换律到后面就会体会到他的强大)

             

            通过以上的结论我们可以假装设计出一个算法(这里我用的是VBScript):

    Const PLAY = &H1021&      '这里生成项(实际上就是CRC-CCITT),注意这里默认首位1是去掉的
    Const TEXT = "123456789"  '这里就是原始数据
    
    Dim L,I,CRC
    Do While L < Len(TEXT)
    '通过循环取得原始数据的每一个字符来计算
        L = L + 1
        CRC = CRC Xor (Asc(Mid(TEXT,L,1)) * &H100&)
        '实际上文中提到的“盒子”专业的说法应该叫做:寄存器。
        '这里取出新的字符出来与CRC寄存器里上一次未和数据进行计算的多项式的部分进行Xor,作为新数据进行下一步处理。
        '机智的你也许发现了:1021这个生成式是16位与一个字节8位不符怎么办?别忘我们还有神器——补零!乘上H100 = 256 = 2 ^ 8
        For I = 0 To 7
            '判断数据最高位是否为 1
            '1 - 左移一位(去掉数据首位1),剩下的部分进行Xor
            '0 - 就左移一位(去掉多余的0)
            If (CRC And &H8000&) Then 
                CRC = (CRC * 2) And &HFFFF& '// 左移
                CRC = CRC Xor PLAY
            Else
                CRC = (CRC * 2) And &HFFFF& '// 左移
            End If
        Next
        CRC = CRC And &HFFFF&
        '限制寄存器内数据大小为16位。
    Loop
    
    WScript.Echo Hex(CRC)

            运行之后成功就可以得出CRC-CCITT (XModem)的标准验证值:31C3(在线计算网站:On-line CRC calculation and free library

    驱动表法

            实际上CRC就像开心消消乐一样,就是不断消除首位数据。这时你想:要是能一口气消除一个字节(8bit)以上的数据那该多好

            一般来讲文艺青年会这么做(CRC的正常计算):

           

            但是2B青年会这么想:可不可以将生成项先Xor了?

            

            我们可以先提前计算出与数据前几位相符的生成项之和再Xor从而到达一口气消掉了多位数据的目的,Xor的乘法交换律允许我们提前计算出需要的数据A Xor B Xor C = A Xor ( B Xor C )

            既然如此干脆直接将前八位0000 0000 ~ 1111 1111(0 ~ 255,一个字节)这个范围所有的生产项的和全部计算存储成表格,等计算的时候直接取出数据的首字节出来作为索引找到对应表格的中生存项的和与去掉首位字节的数据进行Xor不就可以了吗。

    表的由来

            虽说想法很美好,但是如何实现前8位从0~255所有的生成项之和了?我们来思考一下CRC计算的本质是什么?复读没错是不断地消除首位数据,那么在Xor运算下消除方法就是:数据一样!

             那么我们将0~255这256个数字进行CRC逐位计算后剩下不就是已经消除掉前8位数据的生成项之和吗!(因为前8位数据一样所以被消除了)

             用通俗点语言就是:我们提前将一个字节的CRC验证码计算出来。(实际上这一段语文老师死得早的我构思很久,担心没有上面这段过渡部分会造成读者理解困难,希望大家能Get我的点……Orz)

             以下就是关于CRC16的正序表格计算代码:

    Const PLAY = &H8005&     '这里生成项(CRC16),注意这里默认首位1是去掉的
    ReDim Table(255)         '这里存储表格
    
    '// 计算表格部分
    Dim I,J,Temp
    '对于0~255这256个数字进行CRC计算,并将计算好的CRC验证码按顺序存储在表格(数组)中
    For I = 0 To 255
        Temp = (I * &H100&) And &HFFFF&
        For J = 0 To 7
            If (Temp And &H8000) Then
                Temp = (Temp * 2) And &HFFFF&
                Temp = Temp Xor PLAY
            Else
                Temp = (Temp * 2) And &HFFFF&
            End If
        Next
        Table(I) = Temp And &HFFFF&
    Next
    
    '// 输出CRC16表格代码(使用VbsEdit的调试)
    Dim y,x,u
    Debug.WriteLine "Dim CRC16_Table:CRC16_Table = Array( _"
    For y = 1 To 64
        For x = 1 To 4
            Debug.Write "&H",String(4 - Len(Hex(Table(u))),"0"),Hex(Table(u)),"&"
            If u <> 255 Then Debug.Write ", " Else Debug.Write " "
            u = u + 1
        Next
        Debug.WriteLine "_"
    Next
    Debug.WriteLine ")"
    

             代码无误的话,应该会在调试框中得到如下代码(作为验证可以看看):

    Dim CRC16_Table:CRC16_Table = Array( _
    &H0000&, &H8005&, &H800F&, &H000A&, _
    &H801B&, &H001E&, &H0014&, &H8011&, _
    &H8033&, &H0036&, &H003C&, &H8039&, _
    &H0028&, &H802D&, &H8027&, &H0022&, _
    &H8063&, &H0066&, &H006C&, &H8069&, _
    &H0078&, &H807D&, &H8077&, &H0072&, _
    &H0050&, &H8055&, &H805F&, &H005A&, _
    &H804B&, &H004E&, &H0044&, &H8041&, _
    &H80C3&, &H00C6&, &H00CC&, &H80C9&, _
    &H00D8&, &H80DD&, &H80D7&, &H00D2&, _
    &H00F0&, &H80F5&, &H80FF&, &H00FA&, _
    &H80EB&, &H00EE&, &H00E4&, &H80E1&, _
    &H00A0&, &H80A5&, &H80AF&, &H00AA&, _
    &H80BB&, &H00BE&, &H00B4&, &H80B1&, _
    &H8093&, &H0096&, &H009C&, &H8099&, _
    &H0088&, &H808D&, &H8087&, &H0082&, _
    &H8183&, &H0186&, &H018C&, &H8189&, _
    &H0198&, &H819D&, &H8197&, &H0192&, _
    &H01B0&, &H81B5&, &H81BF&, &H01BA&, _
    &H81AB&, &H01AE&, &H01A4&, &H81A1&, _
    &H01E0&, &H81E5&, &H81EF&, &H01EA&, _
    &H81FB&, &H01FE&, &H01F4&, &H81F1&, _
    &H81D3&, &H01D6&, &H01DC&, &H81D9&, _
    &H01C8&, &H81CD&, &H81C7&, &H01C2&, _
    &H0140&, &H8145&, &H814F&, &H014A&, _
    &H815B&, &H015E&, &H0154&, &H8151&, _
    &H8173&, &H0176&, &H017C&, &H8179&, _
    &H0168&, &H816D&, &H8167&, &H0162&, _
    &H8123&, &H0126&, &H012C&, &H8129&, _
    &H0138&, &H813D&, &H8137&, &H0132&, _
    &H0110&, &H8115&, &H811F&, &H011A&, _
    &H810B&, &H010E&, &H0104&, &H8101&, _
    &H8303&, &H0306&, &H030C&, &H8309&, _
    &H0318&, &H831D&, &H8317&, &H0312&, _
    &H0330&, &H8335&, &H833F&, &H033A&, _
    &H832B&, &H032E&, &H0324&, &H8321&, _
    &H0360&, &H8365&, &H836F&, &H036A&, _
    &H837B&, &H037E&, &H0374&, &H8371&, _
    &H8353&, &H0356&, &H035C&, &H8359&, _
    &H0348&, &H834D&, &H8347&, &H0342&, _
    &H03C0&, &H83C5&, &H83CF&, &H03CA&, _
    &H83DB&, &H03DE&, &H03D4&, &H83D1&, _
    &H83F3&, &H03F6&, &H03FC&, &H83F9&, _
    &H03E8&, &H83ED&, &H83E7&, &H03E2&, _
    &H83A3&, &H03A6&, &H03AC&, &H83A9&, _
    &H03B8&, &H83BD&, &H83B7&, &H03B2&, _
    &H0390&, &H8395&, &H839F&, &H039A&, _
    &H838B&, &H038E&, &H0384&, &H8381&, _
    &H0280&, &H8285&, &H828F&, &H028A&, _
    &H829B&, &H029E&, &H0294&, &H8291&, _
    &H82B3&, &H02B6&, &H02BC&, &H82B9&, _
    &H02A8&, &H82AD&, &H82A7&, &H02A2&, _
    &H82E3&, &H02E6&, &H02EC&, &H82E9&, _
    &H02F8&, &H82FD&, &H82F7&, &H02F2&, _
    &H02D0&, &H82D5&, &H82DF&, &H02DA&, _
    &H82CB&, &H02CE&, &H02C4&, &H82C1&, _
    &H8243&, &H0246&, &H024C&, &H8249&, _
    &H0258&, &H825D&, &H8257&, &H0252&, _
    &H0270&, &H8275&, &H827F&, &H027A&, _
    &H826B&, &H026E&, &H0264&, &H8261&, _
    &H0220&, &H8225&, &H822F&, &H022A&, _
    &H823B&, &H023E&, &H0234&, &H8231&, _
    &H8213&, &H0216&, &H021C&, &H8219&, _
    &H0208&, &H820D&, &H8207&, &H0202& _
    )
    

             我们可以以空间换取时间,直接将CRC表格计算好作为一个常数数组使用。

     

             得到表格之后回到驱动表法的计算:取出CRC寄存器中的首位字节,然后将CRC左移去掉首字节然后取出一字节新数据装入CRC低端空出字节中,根据取出首字节找到对应表格中的生成项之和与CRC寄存器进行Xor,然后重复这个步骤直到数据全部取完计算完。要注意的是:因为驱动表法是一个一个字节计算,所以他必须计算之前在原始数据上补零(不能像直接计算法那样通过逐位左移方式自己完成补零)。我们来看一下代码是如何实现的:

    Dim CRC16_Table                     '这里表格我们直接就套用上一节我们计算好的数据
    Const TEXT = "123456789"            '这里就是原始数据
    
    '这里要注意驱动表法没有逐位补零,所以我们要手动在数据末尾增加两个字节的零
    Dim str:str = TEXT & String(2,Chr(0))
    
    Dim L,I,T,CRC
    '初始化CRC寄存器值为0
    CRC = 0
    Do While L < Len(str)
        L = L + 1
        '取出CRC寄存器中的首字节
        T = (CRC And &HFF00&) / &H100
        '左移数据一个字节(就是去掉寄存器中的首字节),并在空出的字节放入新的数据,限制寄存器大小为两个字节。
        CRC = ((CRC * &H100&) Or Asc(Mid(str,L,1))) And &HFFFF&
        '将已经去掉首字节的CRC寄存器与对应的表格生成项之和进行Xor
        CRC = CRC Xor CRC16_Table(T)
        '当然也可以直接将上面三句缩写成一句:
        'CRC = (((CRC * 256) Or Asc(Mid(str,L,1))) And &HFFFF&) Xor Table16((CRC And &HFF00&) / &H100)
    Loop
    
    '限制CRC大小为两个字节
    CRC = CRC And &HFFFF&
    
    WScript.Echo Hex(CRC)
    

             输出结果为:FEE8(注意这个值并不是标准的CRC16验证码,后面我们还会讲到如何修改它。)

    直驱动法

             这个部分的思路完全来自poiu_elab大佬的【脑冻结】CRC我就拿下了,感谢大佬的分享!

             我们不妨用大佬的列子看看(丑不要脸的偷懒):

            

             我们针对31 32 33 34进行CRC-CCITT驱动表法的计算,着重观察每次查询表格的索引(也就是黄色部分),你会发现实际上索引就是原始数据与寄存器前2位数据Xor计算的结果。

             比如第一次查询时,寄存器为0与原始数据31 Xor得到查表的索引31,查得26 72并存入寄存器内;遇到第二个原始数据32与寄存器内的26进行Xor得到14,查得52 B5由于寄存器左移一个字节于是上一次72移到高位与52进行Xor得到20,于是现在寄存器内就是20(72 Xor 52) B5;遇到第三个原始数据33与寄存器内20(72 xor 52)进行Xor,得到13……重复这个过程直到查到最后一个原始数据为止。

             通过上面的步骤我们可以整理出直驱动表法的算法:

             伪语言版:

             循环:取得字符

                      寄存器 = 去掉首位字节的寄存器 Xor表格[寄存器的首字节 Xor 取出的原始数据]

            C语言版 CRC32直驱表法:

             While(len--)

                    r=(r<<8)^t[(r>>24)^*p++];

             我个人的总结直驱动表法实际上就是将驱动表法再一次简化,将数据输入和表格查询有机的结合在一起,这么做好处可以不用在原始数据上补零,方便持续计算CRC(不停的加入新的数据)。

             由于vbscript对于32位数据进行四则运算(乘法补零)会溢出,所以不妨为我们尝试一下寄存器分段处理(就是将寄存器分成两个部分处理来解决数据溢出的问题),代码如下:

    Dim Table_M(255)         '表格的高位
    Dim Table_L(255)         '表格的低位
    
    '// CRC32正序表格计算
    Const PLOY_M = &H04C1&   '生成项高位,CRC32生成项:04C11DB7
    Const PLOY_L = &H1DB7&   '生成项低位
    Dim I,J,CRC_M,CRC_L
    For I = 0 To 255
        CRC_M = (I * &H100&) And &HFFFF&
        CRC_L = 0
        For J = 0 To 7
            If (CRC_M And &H8000) Then
                '// 左移
                CRC_M = (CRC_M And &H7FFF) * &H2
                CRC_M = CRC_M Xor ((CRC_L And &H8000&) / &H8000&) '低位寄存器中高位会移动到高位寄存器中的低位
                CRC_L = (CRC_L And &H7FFF) * &H2
                '// Xor计算
                CRC_M = CRC_M Xor PLOY_M
                CRC_L = CRC_L Xor PLOY_L
            Else
                '// 左移
                CRC_M = (CRC_M And &H7FFF) * &H2
                CRC_M = CRC_M Xor ((CRC_L And &H8000&) / &H8000&)
                CRC_L = (CRC_L And &H7FFF) * &H2
            End If
            Table_M(I) = CRC_M
            Table_L(I) = CRC_L
        Next
    Next
    
    '// CRC32计算部分
    Const TEXT = "123456789"  '原始数据
    CRC_M = 0
    CRC_L = 0
    Dim T
    Do While L < Len(TEXT)
        L = L + 1
        '计算出查询表格的索引
        T = ((CRC_M And &HFF00&) / &H100&) Xor Asc(Mid(TEXT,L,1))
        '分别计算出高位、低位寄存器,要注意的是低位寄存器中高位会移动到高位寄存器中的低位。
        CRC_M = ((CRC_M And &HFF&) * &H100) Xor ((CRC_L And &HFF00&) / &H100&) Xor Table_M(T)
        CRC_L = ((CRC_L And &HFF&) * &H100) Xor Table_L(T)
    Loop
    
    '记得输出不足CRC寄存器长度的数据时在前面补零
    Debug.WriteLine String(4 - Len(Hex(CRC_M)),"0"),Hex(CRC_M),String(4 - Len(Hex(CRC_L)),"0"),Hex(CRC_L)
    

             输出结果为:89A1897F

    CRC参数模型

             细心的朋友可能发现我们上一节计算出的CRC32验证值是不对的,为了方便机器更好的计算CRC所以制定一些规则,称为CRC参数模型。我们一睹CRC32模型的芳容:

            

             Width:代表生成项长度

             Poly:生成项

             Init:寄存器计算前的初始值,其实初始值是为了保存数据之前零(CRC计算特性是省略开头的零)

             RefIn:输入原始数据进行二进制数据反转,因为数据输入是一个字节一个字节,所以反转也是一个字节,如图所示:

            

             RefOut:最后输出的CRC数据进行数据反转,注意是整个CRC数据进行反转(其实就是对CRC寄存器进行反转)

             XorOut:对已经RefOut的CRC数据进行Xor处理,方式和Init一样。

             Check:是对字符串“123456789”CRC计算的验证值,作为参考看看自己的程序计算是否有误。

             根据这个模型,我们对上一节的CRC32算法再次魔改:

    Dim Table_M:Table_M = Array( _  ' CRC32表格高位,由于篇幅有限请自行补齐代码
    Dim Table_L:Table_L = Array( _  ' CRC32表格低位,由于篇幅有限请自行补齐代码
    
    
    '颠倒二进制函数
    Public Function RevBin(ByVal Value,ByVal lLen)
        RevBin = 0
        If IsNumeric(Value) And Value Then
            lLen = lLen - 1
            Dim I,REG
            For I = 0 To lLen
                If ((2^I) And Value) Then REG = REG Or 2^(lLen - I)
            Next
            RevBin = REG
        End If
    End Function
    
    '原始数据
    Const TEXT = "123456789" 
    
    'Init:初始化寄存器
    CRC_M = &HFFFF&
    CRC_L = &HFFFF&
    
    '计算CRC部分
    Dim T
    Do While L < Len(TEXT)
        L = L + 1
        'RefIn:注意这里的对一个字节(8bit)的反转(我曾经在这里被坑过)
        T = ((CRC_M And &HFF00&) / &H100) Xor RevBin(Asc(Mid(TEXT,L,1)),8)
        CRC_M = ((CRC_M And &HFF) * &H100) Xor ((CRC_L And &HFF00&) / &H100) Xor Table_M(T)
        CRC_L = ((CRC_L And &HFF) * &H100) Xor Table_L(T)
    Loop
    
    'RefOut:反转计算出来的CRC值
    Dim Temp
    Temp = RevBin(CRC_L,16)
    CRC_L = RevBin(CRC_M,16)
    CRC_M = Temp
    
    'XorOut:最后一次Xor
    CRC_M = CRC_M Xor &HFFFF&
    CRC_L = CRC_L Xor &HFFFF&
    
    '输出CRC验证值
    Debug.WriteLine String(4 - Len(Hex(CRC_M)),"0"),Hex(CRC_M),String(4 - Len(Hex(CRC_L)),"0"),Hex(CRC_L)
    

             终于得到正确的CRC32验证码:CBF43926

    进一步的优化:镜像CRC直驱表法算法

             虽然可以计算出CRC32,但是每次都要颠倒输入、输出的值这显十分浪费性能。我们可以不可以将这个算法直接镜像,从而避免对其输入、输出的逆转,只需要对表格进行逆转和相反的数据位移方向即可。

            

             那么新的问题已经出现,我们怎能停滞不前:这逆转的表格咋算啊?!

             答案就是:要用魔法(逆转)去打败魔法(逆转):将生成项:04C11DB7逆转为EDB88320,新数据插入的方向为数据低位(新数据不需要逆转只需要通过它的值计算出对应的生成项之和),数据位移方向向右(这个就是逆转的核心)。我们可以达到以下代码: 

    Dim Table_M(255)         '表格的高位
    Dim Table_L(255)         '表格的低位
    
    '// CRC32逆序表格计算,CRC32生成项:EDB88320
    Const PLOY_M = &HEDB8&   '生成项高位
    Const PLOY_L = &H8320&   '生成项低位
    Dim I,J,CRC_M,CRC_L
    For I = 0 To 255
        CRC_M = 0
        CRC_L = I
        For J = 0 To 7
            If (CRC_L And &H1) Then
                '// 右移
                CRC_L = (CRC_L And &HFFFE&) / &H2
                CRC_L = CRC_L Xor ((CRC_M And &H1) * &H8000&) '高位寄存器中低位会移动到低位寄存器中的高位
                CRC_M = (CRC_M And &HFFFE&) / &H2
                '// Xor计算
                CRC_M = CRC_M Xor PLOY_M
                CRC_L = CRC_L Xor PLOY_L
            Else
                '// 右移
                CRC_L = (CRC_L And &HFFFE&) / &H2
                CRC_L = CRC_L Xor ((CRC_M And &H1) * &H8000&)  
                CRC_M = (CRC_M And &HFFFE&) / &H2
            End If
        Next
        Table_M(I) = CRC_M
        Table_L(I) = CRC_L
    Next
    
    '//输出逆序表格代码
    Public Sub Print(ByVal Name,ByVal lTable)
        Dim y,x,u
        Debug.WriteLine Name & " = Array( _"
        For y = 1 To 32
            For x = 1 To 8
                Debug.Write "&H",String(4 - Len(Hex(lTable(u))),"0"),Hex(lTable(u)),"&"
                If u <> 255 Then Debug.Write ", " Else Debug.Write " "
                u = u + 1
            Next
            Debug.WriteLine "_"
        Next
        Debug.WriteLine ")"
    End Sub
    
    Call Print("CRC32Table_MSB",Table_M)
    Call Print("CRC32Table_LSB",Table_L)
    

             计算出表格代码根据上图的镜像算法,我们可以将新数据xor到寄存器最低位取得表格索引,然后两个寄存器左移一个字节计算索引对应的逆转的生成项之和。封装一下代码就是这样的:

    Class C_CRC
        '初始化类载入数据
        Private m_CRC32Table_MSB
        Private m_CRC32Table_LSB
        Private Sub Class_Initialize()
            m_CRC32Table_MSB = Array( _
            &H0000&, &H7707&, &HEE0E&, &H9909&, &H076D&, &H706A&, &HE963&, &H9E64&, _
            &H0EDB&, &H79DC&, &HE0D5&, &H97D2&, &H09B6&, &H7EB1&, &HE7B8&, &H90BF&, _
            &H1DB7&, &H6AB0&, &HF3B9&, &H84BE&, &H1ADA&, &H6DDD&, &HF4D4&, &H83D3&, _
            &H136C&, &H646B&, &HFD62&, &H8A65&, &H1401&, &H6306&, &HFA0F&, &H8D08&, _
            &H3B6E&, &H4C69&, &HD560&, &HA267&, &H3C03&, &H4B04&, &HD20D&, &HA50A&, _
            &H35B5&, &H42B2&, &HDBBB&, &HACBC&, &H32D8&, &H45DF&, &HDCD6&, &HABD1&, _
            &H26D9&, &H51DE&, &HC8D7&, &HBFD0&, &H21B4&, &H56B3&, &HCFBA&, &HB8BD&, _
            &H2802&, &H5F05&, &HC60C&, &HB10B&, &H2F6F&, &H5868&, &HC161&, &HB666&, _
            &H76DC&, &H01DB&, &H98D2&, &HEFD5&, &H71B1&, &H06B6&, &H9FBF&, &HE8B8&, _
            &H7807&, &H0F00&, &H9609&, &HE10E&, &H7F6A&, &H086D&, &H9164&, &HE663&, _
            &H6B6B&, &H1C6C&, &H8565&, &HF262&, &H6C06&, &H1B01&, &H8208&, &HF50F&, _
            &H65B0&, &H12B7&, &H8BBE&, &HFCB9&, &H62DD&, &H15DA&, &H8CD3&, &HFBD4&, _
            &H4DB2&, &H3AB5&, &HA3BC&, &HD4BB&, &H4ADF&, &H3DD8&, &HA4D1&, &HD3D6&, _
            &H4369&, &H346E&, &HAD67&, &HDA60&, &H4404&, &H3303&, &HAA0A&, &HDD0D&, _
            &H5005&, &H2702&, &HBE0B&, &HC90C&, &H5768&, &H206F&, &HB966&, &HCE61&, _
            &H5EDE&, &H29D9&, &HB0D0&, &HC7D7&, &H59B3&, &H2EB4&, &HB7BD&, &HC0BA&, _
            &HEDB8&, &H9ABF&, &H03B6&, &H74B1&, &HEAD5&, &H9DD2&, &H04DB&, &H73DC&, _
            &HE363&, &H9464&, &H0D6D&, &H7A6A&, &HE40E&, &H9309&, &H0A00&, &H7D07&, _
            &HF00F&, &H8708&, &H1E01&, &H6906&, &HF762&, &H8065&, &H196C&, &H6E6B&, _
            &HFED4&, &H89D3&, &H10DA&, &H67DD&, &HF9B9&, &H8EBE&, &H17B7&, &H60B0&, _
            &HD6D6&, &HA1D1&, &H38D8&, &H4FDF&, &HD1BB&, &HA6BC&, &H3FB5&, &H48B2&, _
            &HD80D&, &HAF0A&, &H3603&, &H4104&, &HDF60&, &HA867&, &H316E&, &H4669&, _
            &HCB61&, &HBC66&, &H256F&, &H5268&, &HCC0C&, &HBB0B&, &H2202&, &H5505&, _
            &HC5BA&, &HB2BD&, &H2BB4&, &H5CB3&, &HC2D7&, &HB5D0&, &H2CD9&, &H5BDE&, _
            &H9B64&, &HEC63&, &H756A&, &H026D&, &H9C09&, &HEB0E&, &H7207&, &H0500&, _
            &H95BF&, &HE2B8&, &H7BB1&, &H0CB6&, &H92D2&, &HE5D5&, &H7CDC&, &H0BDB&, _
            &H86D3&, &HF1D4&, &H68DD&, &H1FDA&, &H81BE&, &HF6B9&, &H6FB0&, &H18B7&, _
            &H8808&, &HFF0F&, &H6606&, &H1101&, &H8F65&, &HF862&, &H616B&, &H166C&, _
            &HA00A&, &HD70D&, &H4E04&, &H3903&, &HA767&, &HD060&, &H4969&, &H3E6E&, _
            &HAED1&, &HD9D6&, &H40DF&, &H37D8&, &HA9BC&, &HDEBB&, &H47B2&, &H30B5&, _
            &HBDBD&, &HCABA&, &H53B3&, &H24B4&, &HBAD0&, &HCDD7&, &H54DE&, &H23D9&, _
            &HB366&, &HC461&, &H5D68&, &H2A6F&, &HB40B&, &HC30C&, &H5A05&, &H2D02& )
            m_CRC32Table_LSB = Array( _
            &H0000&, &H3096&, &H612C&, &H51BA&, &HC419&, &HF48F&, &HA535&, &H95A3&, _
            &H8832&, &HB8A4&, &HE91E&, &HD988&, &H4C2B&, &H7CBD&, &H2D07&, &H1D91&, _
            &H1064&, &H20F2&, &H7148&, &H41DE&, &HD47D&, &HE4EB&, &HB551&, &H85C7&, _
            &H9856&, &HA8C0&, &HF97A&, &HC9EC&, &H5C4F&, &H6CD9&, &H3D63&, &H0DF5&, _
            &H20C8&, &H105E&, &H41E4&, &H7172&, &HE4D1&, &HD447&, &H85FD&, &HB56B&, _
            &HA8FA&, &H986C&, &HC9D6&, &HF940&, &H6CE3&, &H5C75&, &H0DCF&, &H3D59&, _
            &H30AC&, &H003A&, &H5180&, &H6116&, &HF4B5&, &HC423&, &H9599&, &HA50F&, _
            &HB89E&, &H8808&, &HD9B2&, &HE924&, &H7C87&, &H4C11&, &H1DAB&, &H2D3D&, _
            &H4190&, &H7106&, &H20BC&, &H102A&, &H8589&, &HB51F&, &HE4A5&, &HD433&, _
            &HC9A2&, &HF934&, &HA88E&, &H9818&, &H0DBB&, &H3D2D&, &H6C97&, &H5C01&, _
            &H51F4&, &H6162&, &H30D8&, &H004E&, &H95ED&, &HA57B&, &HF4C1&, &HC457&, _
            &HD9C6&, &HE950&, &HB8EA&, &H887C&, &H1DDF&, &H2D49&, &H7CF3&, &H4C65&, _
            &H6158&, &H51CE&, &H0074&, &H30E2&, &HA541&, &H95D7&, &HC46D&, &HF4FB&, _
            &HE96A&, &HD9FC&, &H8846&, &HB8D0&, &H2D73&, &H1DE5&, &H4C5F&, &H7CC9&, _
            &H713C&, &H41AA&, &H1010&, &H2086&, &HB525&, &H85B3&, &HD409&, &HE49F&, _
            &HF90E&, &HC998&, &H9822&, &HA8B4&, &H3D17&, &H0D81&, &H5C3B&, &H6CAD&, _
            &H8320&, &HB3B6&, &HE20C&, &HD29A&, &H4739&, &H77AF&, &H2615&, &H1683&, _
            &H0B12&, &H3B84&, &H6A3E&, &H5AA8&, &HCF0B&, &HFF9D&, &HAE27&, &H9EB1&, _
            &H9344&, &HA3D2&, &HF268&, &HC2FE&, &H575D&, &H67CB&, &H3671&, &H06E7&, _
            &H1B76&, &H2BE0&, &H7A5A&, &H4ACC&, &HDF6F&, &HEFF9&, &HBE43&, &H8ED5&, _
            &HA3E8&, &H937E&, &HC2C4&, &HF252&, &H67F1&, &H5767&, &H06DD&, &H364B&, _
            &H2BDA&, &H1B4C&, &H4AF6&, &H7A60&, &HEFC3&, &HDF55&, &H8EEF&, &HBE79&, _
            &HB38C&, &H831A&, &HD2A0&, &HE236&, &H7795&, &H4703&, &H16B9&, &H262F&, _
            &H3BBE&, &H0B28&, &H5A92&, &H6A04&, &HFFA7&, &HCF31&, &H9E8B&, &HAE1D&, _
            &HC2B0&, &HF226&, &HA39C&, &H930A&, &H06A9&, &H363F&, &H6785&, &H5713&, _
            &H4A82&, &H7A14&, &H2BAE&, &H1B38&, &H8E9B&, &HBE0D&, &HEFB7&, &HDF21&, _
            &HD2D4&, &HE242&, &HB3F8&, &H836E&, &H16CD&, &H265B&, &H77E1&, &H4777&, _
            &H5AE6&, &H6A70&, &H3BCA&, &H0B5C&, &H9EFF&, &HAE69&, &HFFD3&, &HCF45&, _
            &HE278&, &HD2EE&, &H8354&, &HB3C2&, &H2661&, &H16F7&, &H474D&, &H77DB&, _
            &H6A4A&, &H5ADC&, &H0B66&, &H3BF0&, &HAE53&, &H9EC5&, &HCF7F&, &HFFE9&, _
            &HF21C&, &HC28A&, &H9330&, &HA3A6&, &H3605&, &H0693&, &H5729&, &H67BF&, _
            &H7A2E&, &H4AB8&, &H1B02&, &H2B94&, &HBE37&, &H8EA1&, &HDF1B&, &HEF8D& )
        End Sub
        
        '// 计算CRC部分
        Public Function CRC32(ByVal Value)
            Dim CRC_MSB,CRC_LSB
            CRC_MSB = &HFFFF&
            CRC_LSB = &HFFFF&
            
            Dim StrLen:StrLen = Len(Value)
            Dim FirstByte,L
            Do While L < StrLen
                L = L + 1
                FirstByte = (CRC_LSB And &HFF&) Xor Asc(Mid(Value,L,1))
                CRC_LSB = ((CRC_LSB And &HFF00&) / &H100) Xor ((CRC_MSB And &HFF&) * &H100&) Xor m_CRC32Table_LSB(FirstByte)
                CRC_MSB = ((CRC_MSB And &HFF00&) / &H100) Xor m_CRC32Table_MSB(FirstByte)
            Loop
            
            CRC_MSB = CRC_MSB Xor &HFFFF&
            CRC_LSB = CRC_LSB Xor &HFFFF&
            
            CRC32 = String(4 - Len(Hex(CRC_MSB)),"0") & Hex(CRC_MSB) & String(4 - Len(Hex(CRC_LSB)),"0") & Hex(CRC_LSB)
        End Function
    End Class
    
    Dim CRC:Set CRC = New C_CRC
    WScript.Echo CRC.CRC32("123456789")
    

             到这里教程就完全结束了,当然我这个代码完全没有效率,纯粹写出演示闹着玩(笑)。如果是想用vbs进行CRC计算的话,我推荐Demon大佬的这篇Blog《用VBS实现PHP的crc32函数》。使用MSScriptControl.ScriptControl对象调用js就可以十分方便的实现数据数据左右移。可读性远比我这篇代码要高的多,推荐去读一读方便理解。

    其他博客推荐

             虽说我码这么多字但我还是没有自信能保证各位一定能懂(强行语文老师背锅),所以我个人觉得写的不错的几篇博客(也是我学习的博客),希望能对大家有用。

      1. 循环冗余校验(CRC)算法入门引导:https://blog.csdn.net/liyuanbhu/article/details/7882789
      2. 我学习CRC32、CRC16、CRC原理和算法的总结(与WINRAR结果一致):https://wenku.baidu.com/view/fb791c0203d8ce2f006623f5.html
      3. 【脑冻结】CRC我就拿下了:https://www.cnblogs.com/poiu-elab/archive/2012/10/22/2734715.html
      4. CRC的基本原理详解:https://blog.csdn.net/dream_1996/article/details/73588269
      5. 循环冗余检验 (CRC) 算法原理:http://www.cnblogs.com/esestt/archive/2007/08/09/848856.html
      6. 你真的搞明白CRC的计算过程了吗?:http://blog.chinaaet.com/wuyage/p/5100049902
      7. CRC检错技术原理:https://www.cnblogs.com/forget406/p/5533133.html
      8. CRC算法详解与c源码:https://wenku.baidu.com/view/353fcd0eaaea998fcd220e0a.html
      9. 最通俗的CRC校验原理剖析:http://blog.51cto.com/winda/1063951
      10. VB的CRC32校验代码:https://blog.csdn.net/zdingyun/article/details/46839779
      11. CRC校验码原理、实例、手动计算:https://www.cnblogs.com/bugutian/p/6221783.html
      12. 如何计算CRC32校验和?:https://codeday.me/bug/20170801/50763.html
      13. CRC算法与实现:https://blog.csdn.net/ncdawen/article/details/633014

    再推荐两个在线计算网站方便验证程序:

      1. CRC(循环冗余校验)在线计算:http://www.ip33.com/crc.html
      2. On-line CRC calculation and free library:https://www.lammertbies.nl/comm/info/crc-calculation.html

             当然如果你E文比较好的话,还是推荐Ross Williams的《A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS》 http://www.ross.net/crc/download/crc_v3.txt

     

    哦,对了!故事的最后:王大麻子其实喜欢他们班的班长——小强

    展开全文
  • CRC原理及其逆向破解方法: 介绍: 这篇短文包含CRC原理介绍和其逆向分析方法,很多程序员和破解者不是很清楚了解CRC的工作原理,而且几乎没人知道如何逆向分析它的方法,事实上它是非常有用的.首先,这篇教程教你...

    CRC原理及其逆向破解方法:

    介绍:

      这篇短文包含CRC原理介绍和其逆向分析方法,很多程序员和破解者不是很清楚了解
    CRC的工作原理,而且几乎没人知道如何逆向分析它的方法,事实上它是非常有用的.
    首先,这篇教程教你一般如何计算CRC,你可以将它用在数据代码保护中.第二,主要是
    介绍如何逆向分析CRC-32,你可以以此来分析程序中的CRC保护(象反病毒编码).当然
    有很多有效的工具用来对付CRC,但我怀疑它是否会说明原理.
      我要告诉你,这篇短文里中应用了很多数学知识,这不会影响一些人,而且会被一般的
    程序员与逆向分析者很好理解.为什么?那么如果你不知道数学是如何被应用在CRC中,
    我建议你可以停止继续学习了.所以我假定你们(读者)都是具备二进制算术知识的.

    第一部分:CRC 介绍,CRC是什么和计算CRC的方法. 

    循环冗余码 CRC

      我们都知道CRC.甚至你没有印象,但当你想到那些来自诸如RAR,ZIP等压缩软件发给你
    由于错误连接和其他一些意外原因导致的文件错误的恼人的消息时,你就会知道.CRC是块
    数据的计算值,比如对每一个文件进行压缩.在一个解压缩过程中,程序会从新计算解压文件
    的CRC值,并且将之与从文件中读取的CRC值进行比对,如果值相同,那么正确.在CRC-32中,
    会有1/2^32的可能性发生对确认数据更改的校验错误.   
      很多人认为CRC就是循环冗余校验,假如CRC真的就是循环冗余校验,那么很多人都错用了
    这个术语.你不能说"这个程序的CRC是12345678".人们也常说某一个程序有CRC校验,而不
    说是 "循环冗余校验" 校验.结论:CRC 代表循环冗余码,而不是循环冗余校验. 
      计算是如何完成的呢?好,主要的想法就是将一个文件看成一个被一些数字分割的很长的
    位字串,这里会有一个余数---CRC!你总会有一个余数(可以是0),它至多比除数小一.
    (9/3=3 余数=0 ; (9+2)/3=3 余数=2)
    (或者它本身就包含一个除数在其中).
      在这里CRC计算方法与除法有一点点区别,除法就是将被减数重复的减去除数X次,然后留下
    余数.如果你希望得到原值,那么你就要把除数乘上X次,然后加上余数.
      CRC计算使用特殊的减法与加法完成的.也就是一种新的"算法".计算中每一位计算的进位值
    被"遗忘"了. 
    看如下两个例子,1是普通减法,2和3是特殊的.
         -+
    (1) 1101  (2) 1010  1010  (3) 0+0=0  0-0=0
        1010-     1111+ 1111-     0+1=1 *0-1=1
        ----      ----  ----      1+0=1  1-0=1
        0011      0101  0101     *1+1=0  1-1=0
      在(1)中,右数第二列可以看成是0-1=-1,因此要从高位借1,就变成(10+0)-1=1.(这就象普通
    的'by-paper'十进制减法).特例(2,3)中,1+1会有正常的结果10,'1'是计算后的进位.这个值
    被忽略了.特殊情况0-1应该有正常结果'-1'就要退到下一位.这个值也被忽略了.假如你对编程
    有一定了解,这就象,XOR 操作或者更好.
      现在来看一个除法的例子:

    在普通算法中:
    1001/1111000\1101 13            9/120\13
         1001    -                    09  -|
         ----                         --   |
          1100                         30  |
          1001    -                    27  -
          ----                         --
           0110                         3 -> 余数
           0000    -
           ----
            1100
            1001    -
            ----
             011 -> 3, 余数

    在CRC算法中:
    1001/1111000\1110               9/120\14 余数为 6
         1001    -
         ----
          1100
          1001    -
          ----
           1010
           1001    -
           ----
            0110
            0000    -
            ----
             110 -> 余数
    (例 3)

      这个除法的商并不重要,也没必要去记住,因为他们仅仅是一组无关紧要的位串.真正
    重要的是余数!它就是这个值,可以说比原文件还重要的值,他就是基本的CRC.


    过度到真正的CRC码计算.

      进行一个CRC计算我们需要选则一个除数,从现在起我们称之为"poly".宽度W就是最高位
    的位置,所以这个poly 1001的W 是3,而不是4.注意最高位总是1,当你选定一个宽度,那么你只
    需要选择低W各位的值. 
      假如我们想计算一个位串的CRC码,我们想确定每一个位都被处理过,因此,我们要在目标
    位串后面加上W个0位.在此例中,我们假设位串为1111.请仔细分析下面一个例子:

    Poly                = 10011, 宽度 W=4
    位串                Bitstring
    Bitstring + W zeros = 110101101 + 0000

    10011/1101011010000\110000101 (我们不关心此运算的商)
          10011|||||||| -
          -----||||||||
           10011|||||||
           10011|||||||  -
           -----|||||||
            00001||||||
            00000||||||   -
            -----||||||
             00010|||||
             00000|||||    -
             -----|||||
              00101||||
              00000||||     -
              -----||||
               01010|||
               00000|||      -
               -----|||
                10100||
                10011||       -
                -----||
                 01110|
                 00000|        -
                 -----|
                  11100
                  10011         -
                  -----
                   1111 -> 余数 -> the CRC!
    (例 4)

    重要两点声明如下:
    1.只有当Bitstring的最高位为1,我们才将它与poly做XOR运算,否则我们只是将
      Bitstring左移一位.
    2.XOR运算的结果就是被操作位串bitstring与低W位进行XOR运算,因为最高位总为0.

    算法设计:

      你们都应知道基于位运算的算法是非常慢的而且效率低下.但如果将计算放在每一字节上
    进行,那么效率将大大提高.不过我们只能接受poly的宽度是8的倍数(一个字节;).可以形
    象的看成这样一个宽度为32的poly(W=32):

              3   2   1   0    byte
            +---+---+---+---+
    Pop! <--|   |   |   |   |<-- bitstring with W zero bits added, in this case 32
            +---+---+---+---+
           1<--- 32 bits ---> this is the poly, 4*8 bits

    (figure 1)
      这是一个你用来存放暂时CRC结果的记存器,现在我称它为CRC记存器或者记存器.你从右
    至左移动位串,当从左边移出的位是1,则整个记存器被与poly的低W位进行XOR运算.(此例
    中为32).事实上,我们精确的完成了上面除法所做的事情.


    移动前记存器值为:10110100
    当从右边移入4位时,左边的高4位将被移出,此例中1011将被移出,而1101被移入.

    情况如下:
    当前8位CRC记存器      : 01001101
    刚刚被移出的高4位     : 1011
    我们用此poly          : 101011100, 宽度 W=8

    现在我们用如前介绍的方法来计算记存器的新值.
    顶部  记存器
    ---- --------
    1011 01001101  高四位和当前记存器值
    1010 11100   + (*1) Poly 放在顶部最高位进行XOR运算 (因为那里是1)
    -------------
    0001 10101101 运算结果

    现在我们仍有一位1在高4位:
    0001 10101101  上一步结果
       1 01011100+ (*2) Poly 放在顶部的最低位进行XOR运算 (因为那里是1)
    -------------
    0000 11110001 第二步运算结果
    ^^^^
    现在顶部所有位均为0,所以我们不需要在与poly进行XOR运算

    你可以得到相同的结果如果你先将(*1)与(*2)做XOR然后将结果与记存器值做XOR.
    这就是标准XOR运算的特性:
    (a XOR b) XOR c = a XOR (b XOR c)  由此,推出如下的运算顺序也是正确的.

    1010 11100       poly  (*1)    放在顶部最高位
       1 01011100+   polys (*2)    放在顶部最低位
    -------------
    1011 10111100  (*3) XOR运算结果

    The result (*3)   将(*3)与记存器的值做XOR运算
    1011 10111100
    1011 01001101+    如右:
    -------------
    0000 11110001

    你看到了吗?得到一样的结果!现在(*3)变的重要了,因为顶部为1010则(3)的值总是等于
    10111100(当然是在一定的条件之下)这意味着你可以预先计算出任意顶部位结合的XOR值.
    注意,顶部结果总是0,这就是组合XOR操作导致的结果.(翻译不准确,保留原文)

      现在我们回到figure 1,对每一个顶部字节的值都做移出操作,我们可以预先计算出一个值.
    此例中,它将是一个包含256个double word(32 bit)双字的表.(附录中CRC-32的表).
    (翻译不准确,保留原文) 

    用伪语言表示我们的算法如下:

    While (byte string is not exhausted)
        Begin
        Top = top_byte of register ;
        Register = Register shifted 8 bits left ORred with a new byte from string ;
        Register = Register XORred by value from precomputedTable at position Top ;
        End

    direct table算法:
      上面提到的算法可以被优化.字节串中的字节在被用到之前没有必要经过整个记村器.用
    这个新的算法,我们可以直接用一个字节去XOR一个字节串通过将此字节移出记存器.结果
    指向预先计算的表中的一个值,这个值是用来被记存器的值做XOR运算的. 
      我不十分确切的知道为什么这会得到同样的结果(这需要了解XOR运算的特性),但是这又
    极为便利,因为你无须在你的字节串后填充0字节/位.(如果你知道原理,请告诉我:)
      让我们来实现这个算法:

      +----< byte string  (or file)  字节串,(或是文件)
      |
      v       3   2   1   0    byte  字节
      |     +---+---+---+---+
    XOR---<|   |   |   |   |  Register  记存器
      |     +---+---+---+---+
      |             |
      |            XOR
      |             ^
      v     +---+---|---+---+
      |     |   |   |   |   |  Precomputed table 值表(用来进行操作)
      |     +---+---+---+---+
      +--->-:   :   :   :   :
            +---+---+---+---+
            |   |   |   |   |
            +---+---+---+---+
    (figure 2)

    'reflected' direct Table 算法:

      由于这里有这样一个与之相对应的'反射'算法,事情显得复杂了.一个反射的值/记存器
    就是将它的每一位以此串的中心位为标准对调形成的.例如:0111011001就是1001101110
    的反射串. 
      他们提出'反射'是因为UART(一种操作IO的芯片)发送每一个字节时是先发最没用的0位,
    最后再发最有意义的第七位.这与正常的位置是相逆的. 
      除了信息串不做反射以外,在进行下一步操作前,要将其于的数据都做反射处理.所以在
    计算值表时,位向右移,且poly也是作过反射处理的.当然,在计算CRC时,记存器也要向右
    移,而且值表也必须是反射过的. 

      byte string (or file) -->---+
                                  |    1. 表中每一个入口都是反射的.
        byte  3   2   1   0       V    2. 初始化记存器也是反射的.
            +---+---+---+---+     |    3. 但是byte string中的数据不是反射的,
            |   |   |   |   |>---XOR      因为其他的都做过反射处理了.
            +---+---+---+---+     |
                    |             |
                   XOR            V
                    ^             |
            +---+---|---+---+     |
            |   |   |   |   |     |     值表
            +---+---+---+---+     |
            :   :   :   :   : <---+
            +---+---+---+---+
            |   |   |   |   |
            +---+---+---+---+
    (figure 3)

    我们的算法如下:
    1. 将记存器向右移动一个字节.
    2. 将刚移出的哪个字节与byte string中的新字节做XOR运算,
       得出一个指向值表table[0..255]的索引
    3. 将索引所指的表值与记存器做XOR运算.
    4. 如数据没有全部处理完,则跳到步骤1.


    下面是这个算法的简单的可执行汇编源码:

    完整的CRC-32标准所包含的内容:
    Name            : "CRC-32"
    Width           : 32
    Poly            : 04C11DB7
    Initial value   : FFFFFFFF
    Reflected       : True
    XOR out with    : FFFFFFFF

    作为对你好奇心的奖励, 这里是CRC-16标准: :)
    Name            : "CRC-16"
    Width           : 16
    Poly            : 8005
    Initial value   : 0000
    Reflected       : True
    XOR out with    : 0000

    'XOR out with' 是为了最终得到CRC而用来与记存器最后结果做XOR运算的值.
    假如你想了解一些关于'reversed'逆向CRC poly的话,请看我的参考文章.
     
      我是在16位DOS模式下用的32位编码,因此你会在这个程序中看到很多32位与16位混合
    的编码...当然这是很容易转换成纯32位编码的.注意这个程序是经过完整测试并且能够
    正常运行的.下面的Java 和 C 代码都是由这个汇编代码而来的. 
    底下的这段程序就是用来计算CRC-32 table的:

            xor     ebx, ebx   ;ebx=0, 将被用做一个指针.
    InitTableLoop:
            xor     eax, eax   ;eax=0 为计算新的entry.
            mov     al, bl     ;al<-bl

            ;生成入口.
            xor     cx, cx
    entryLoop:
            test    eax, 1
            jz     no_topbit
            shr     eax, 1
            xor     eax, poly
            jmp    entrygoon
    no_topbit:
            shr     eax, 1
    entrygoon:
            inc     cx
            test    cx, 8
            jz     entryLoop

            mov     dword ptr[ebx*4 + crctable], eax
            inc     bx
            test    bx, 256
            jz     InitTableLoop

    注释:  - crctable 是一个包含256个dword的数组.
           - 由于使用反射算法,EAX被向右移.
           - 因此最低的8位被处理了.

    用Java和C写的代码如下(int is 32 bit):

    for (int bx=0; bx<256; bx++){
      int eax=0;
      eax=eax&0xFFFFFF00+bx&0xFF;      // 就是 'mov al,bl' 指令
      for (int cx=0; cx<8; cx++){
        if (eax&&0x1) {
          eax>>=1;
          eax^=poly;
        }
        else eax>>=1;
      }
      crctable[bx]=eax;
    }

    下面的汇编代码是用来计算CRC-32的:

    computeLoop:
            xor     ebx, ebx
            xor     al, [si]
            mov     bl, al
            shr     eax, 8
            xor     eax, dword ptr[4*ebx+crctable]
            inc     si
            loop   computeLoop

            xor     eax, 0FFFFFFFFh

    注释:  - ds:si 指向将要被处理的byte string信息流.
           - cx 信息流的长度.
           - eax 是当前的CRC.
           - crctable是用来计算CRC的值表.
           - 此例中记存器的初始值为: FFFFFFFF.
           - 要将中间值与FFFFFFFFh做XOR才能得到CRC

    下面是Java和C写的代码:

    for (int cx=0; cx>=8;
       eax^=crcTable[ebx];
    }
    eax^=0xFFFFFFFF;

      现在我们已经完成了本文的第一部分:CRC原理部分,所以如果你希望能够对CRC做更深
    的研究,那么我建议你去读在本文最后给出连接上的资料,我读了.好了,终于到了本文最
    有意思的部分,CRC的逆向分析!


    ------------------------------------------------------------------------------------
    第二部分 CRC的逆向分析:

      我遇到了很多障碍,当我思考如何破解CRC时.我试图使用一些特殊顺序的字节使CRC无效.
    但我没有做到...后来我意识到这种方法是行不同的,因为CRC内建了一些处理过程,无论你
    改变任何位它都不会出问题,真正的CRC就是在不断变化的,总是在变化的.找一些CRC程序,
    你可以自己尝试一下. 
      现在我知道我只能'纠正'在CRC后面的那些我想改变的字节.所以我要构造一个字节序列,
    它可以将CRC转化成任何我想要的样子! 

    具体实现这个想法

    一个字节串?     01234567890123456789012345678901234567890123456789012
    You want to change from  ^  this byte to  ^  this one.

    就是位置9->26.
    同时我们需要额外的4个字节用来在最后恢复原始字节串.

      当你计算CRC-32时,从0-8都没有问题,直到第9位,修补过的字节串会使CRC发生根本的改变.
    即使当走过了第26位,以后的字节都没有改变,你也不可能在得到原始的CRC了,不可能了!你读
    过后面的段落时就会明白为什么.间而言之,当你修改一个字节串时,要保证CRC不变. 

    1. 计算并保存从1~9位的CRC.
    2. 继续计算直到第27位还有额外的4字节并保存结果.
    3. 用1的值来计算新的字节串和额外4字节的CRC(对应patch后的新的CRC值),并将之保存.
    4. 现在我们得到了一个新的CRC,但是我们希望将它还原成原先的CRC,所以我们用逆向算法
       来计算那额外的4字节.

    1~3就是实际的情况,下面你将学到最关键的部分4.


    '反转'CRC-16

      我想,先来介绍计算逆CRC-16对于你来说会简单些.好的,我们现在处在一个恰当的位置,
    在以修改代码后面,就是你想将CRC还原的地方.我们知道原始的CRC(是在patch代码之前计
    算出来的)还有这个当前的记存器值.现在我们的目的就是计算可以改变当前记存器值到原
    始记存器值的两个字节.首先,我们用正常的方法计算这两个未知字节的CRC.我们设他们为
    X,Y.设记存器为a1,a0,只有0不能用来作为变量(00).:)在来看一下我们的CRC算法,figure
    3,更好的理解下面我要做的.

    好,我们开始:

    用这两字节串'X Y' 字节是从左边开始被处理的.
    记存器现在是a1 a0.
    用'+'来表示XOR运算(和第一部分中用的一样)

    处理第一个字节, X:
    a0+X            这是顶部字节的计算结果 (1)
    b1 b0           这是(1)在表中索引对象.
    00 a1           向右移动记存器.
    00+b1 a1+b0     上面两行对应位做XOR运算.

    现在记存器为: (b1) (a1+b0)

    处理第二个字, Y:
    (a1+b0)+Y       此轮顶部字节的计算结果(2)
    c1 c0           这是(2)在表中的索引对象.
    00 b1           向右移动记存器.
    00+c1 b1+c0     上面两行对应位做XOR运算.

    最后记存器就是: (c1) (b1+c0)

    我用一点不同的方法来表示:

    a0 + X      =(1)  在表中指向b1 b0.
    a1 + b0 + Y =(2)  在表中指向c1 c0.
         b1 + c0=d0   记存器中新的低位字节.
              c1=d1   记存器中新的高位字节.
        (1)  (2)

    Wow! 请大家暂时记住上面的信息:)
    别着急, 下面给出一个有具体值的例子.
     
      如果你想要的记存器的值是d1 d0(是原始的CRC),而且你知道在变换之前的记存器的值
    (a1 a0)...那么你将要送如什么样的2个字节进记存器来做CRC计算呢? 
      好了,现在我们的工作应该从幕后走到台前来了.d0一定是bi+c0,并且d1一定是c1...
    但是这到底是怎么回事,我听到你这样问了,你能知道b1和c0的值吗???你还记得哪个值表
    吗?你只需要在表中查找c0 c1这个字的值就可以了因为你知道c1.所以你需要编写一个查
    找程序.假如你找到了这个值,一定要记住这个值的索引,因为这就是找出未知的两个顶部
    字节,举例来说:(1)和(2)!
      所以,现在你找到了c1 c0,那么如何来得到b1 b0呢?如果b1+c0=d0那么b1=d0+c0!如前所
    述,现在你用哪个查找程序在表中查b1 b0的值.现在我们得到了所有计算X和Y所需要的值.
    Cool huh? 
    a1+b0+Y=(2) so Y=a1+b0+(2)
    a0+X=(1)    so X=a0+(1)


    实例.

    让我们来看看这个具体值的例子:
    -register before: (a1=)DE (a0=)AD
    -wanted register: (d1=)12 (d0=)34
    在附录的CRC-16的表中查找以12开头值的入口.这里入口38h的值为12C0.试这找一找是否还
    有以12开头的值的入口.你不可能在找到的,因为我们计算每一中顶部字节组合而得的值的
    入口,一共是256个值,记住!
    现在我们知道(2)=38,c1=12,c0=C0,所以b1=C0+34=F4,现在查找以F4开头的b1的入口.这里
    入口4Fh的值是F441.
    我们还知道  (1)=4F,b1=F4,b0=41,现在所有我们需要的都已经清楚了,接下来我们计算X,Y.

    Y=a1+b0+(2)=DE+41+38=A7
    X=a0+(1)   =AD+4F   =E2

    结论:将CRC 记存器的值从 DEAD 变为 1234 我们需要这两个字节 E2 A7 (以此顺序).

      你看,破解CRC校验你需要反向计算,还有要记住的就是计算过程中的值.当你在用汇编编写
    查找表程序时,要注意intel在小模式中是反向存储值的.现在你可能已经明白如何去破解这个
    CRC-16了...下面介绍如何在CRC-32中实现.


    破解 CRC-32

    现在我们来看CRC-32,和CRC-16是一样容易的(可能一样的不容易你认为).这里你操作的对象
    是4个字节的而不是2字节的.继续向下看,将它与上面CRC-16版本做对比.

    设4字节串 X  Y  Z  W  , 从左边开始处理.
    设记存器为  a3 a2 a1 a0
    注意a3是MSB,而a0是LSB

    处理第一个字节, X:
    a0+X                    这是顶部字节的计算结果(1).
    b3    b2    b1    b0    这是(1)在表中索引对象序列.
    00    a3    a2    a1    右移记存器.
    00+b3 a3+b2 a2+b1 a1+b0 上面两行对应位做XOR运算.

    现在记存器是: (b3) (a3+b2) (a2+b1) (a1+b0)

    Processing second byte, Y:
    (a1+b0)+Y                       这是顶部字节的计算结果(2).
    c3    c2    c1       c0         这是(2)在表中索引对象序列.
    00    b3    a3+b2    a2+b1      右移记存器.
    00+c3 b3+c2 a3+b2+c1 a2+b1+c0   上面两行对应位做XOR运算.

    现在记存器是: (c3) (b3+c2) (a3+b2+c1) (a2+b1+c0)

    Processing third byte, Z:
    (a2+b1+c0)+Z                     这是顶部字节的计算结果(3).
    d3    d2    d1       d0          这是(3)在表中索引对象序列.
    00    c3    b3+c2    a3+b2+c1    右移记存器.
    00+d3 c3+d2 b3+c2+d1 a3+b2+c1+d0 上面两行对应位做XOR运算.

    现在记存器是: (d3) (c3+d2) (b3+c2+d1) (a3+b2+c1+d0)

    Processing fourth byte, W:
    (a3+b2+c1+d0)+W                  这是顶部字节的计算结果(4).
    e3    e2    e1       e0          这是(4)在表中索引对象序列.
    00    d3    c3+d2    b3+c2+d1    右移记存器.
    00+e3 d3+e2 c3+d2+e1 b3+c2+d1+e0 上面两行对应位做XOR运算.

    最后,记存器为: (e3) (d3+e2) (c3+d2+e1) (b3+c2+d1+e0)

    我用一个不同一点的方法来表示:
    a0 + X                  =(1)  在表中指向 b3 b2 b1 b0 
    a1 + b0 + Y             =(2)  在表中指向 c3 c2 c1 c0 
    a2 + b1 + c0 + Z        =(3)  在表中指向 d3 d2 d1 d0 
    a3 + b2 + c1 + d0 + W   =(4)  在表中指向 e4 e3 e2 e1 
         b3 + c2 + d1 + e0  =f0
              c3 + d2 + e1  =f1
                   d3 + e2  =f2
                        e3  =f3
        (1)  (2)  (3)  (4)
    (figure 4)

    这里是用的与CRC-16同样的方法来实现的,我会给出一个具体值的例子.查找用附录中
    CRC-32的值表.

    Take for CRC register before, a3 a2 a1 a0 -> AB CD EF 66
    Take for CRC register after,  f3 f2 f1 f0 -> 56 33 14 78 (wanted value)

    我们开始:

    First byte of entries            entry   value
    e3=f3                     =56 -> 35h=(4) 56B3C423 for e3 e2 e1 e0
    d3=f2+e2      =33+B3      =E6 -> 4Fh=(3) E6635C01 for d3 d2 d1 d0
    c3=f1+e1+d2   =14+C4+63   =B3 -> F8h=(2) B3667A2E for c3 c2 c1 c0
    b3=f0+e0+d1+c2=78+23+5C+66=61 -> DEh=(1) 616BFFD3 for b3 b2 b1 b0

    Now we have all needed values, then
    X=(1)+         a0=         DE+66=B8
    Y=(2)+      b0+a1=      F8+D3+EF=C4
    Z=(3)+   c0+b1+a2=   4F+2E+FF+CD=53
    W=(4)+d0+c1+b2+a3=35+01+7A+6B+AB=8E
    (final computation)

    结论:要将 CRC-32 的记存器的值从 ABCDEF66 改变到 56331478 我们需要这样一个字节
    序列: B8 C4 53 8E


    CRC-32的破解算法

      假如你考虑手动计算这个可以还原CRC记存器的字节序列,那么这将很难变成一个
    简洁的算法. 
     
    看看下面这个最后计算的附加版本:
                                Position
    X =(1) +                a0     0
    Y =(2) +           b0 + a1     1
    Z =(3) +      c0 + b1 + a2     2
    W =(4) + d0 + c1 + b2 + a3     3
    f0= e0 + d1 + c2 + b3          4
    f1= e1 + d2 + c3               5
    f2= e2 + d3                    6
    f3= e3                         7

    (figure 5)
     
      它就等同于figure 4,只不过是一些值/字节被交换了.这种方法可以帮助我们构造一个
    简洁的算法.这里我们用一个8字节的缓冲区,0-3位我们放置a0到a3,4-7位我们放置f0到
    f3.象以前一样,我们用这个已知值e3(由figure 5中得知)在表中查出(e3 e2 e1 e0),并且
    象图5(figure 5)中所示,将它们放到第4位(position 4),我们马上得到了d3的值.因为f2=
    e2+d3,所以f2+e2=d3.又因为(4)已知(入口值),我们照样把它也放到位置3.然后在用d3查表
    得到(d3 d2 d1 d0),同上也将他们放到图中所述位置.同样,由于有f1+e1+d2=c3在位置5上.
      我们继续做直到将b3 b2 b1 b0放到位置1,对了,就是它!  Et voila!
    此时,缓冲区的第3-第0字节中已经包含全部元素,用来计算X~W! 

    算法总结如下:
    1.对于这个8字节的缓冲区,0~3字节放入a0...a3(CRC记存器起始值),4~7字节放入f0...f3
      (目标记存器的值).
    2.取出位置7的已知值,查表得到相应值.
    3.将查出值放如图5相应位置,其实就是做XOR运算.(为了直观,可以拟定此图)
    4.将入口字节放入图中.也是做XOR运算.
    5.继续做2,3两步3次,同时每次降低1个位置 position 5 to 4, 4 to 3 and so on.


    算法的实现:

      现在是时候给出代码了.下面就是用汇编写成的可执行的CRC-32算法(用其他语言也一样
    简单,对于其他的CRC-32标准也一样).注意在汇编中(计算机里)双字在读写操作中顺序都是
    反着的.就是逆向顺序.
    crcBefore       dd (?)
    wantedCrc       dd (?)
    buffer          db 8 dup (?)

            mov     eax, dword ptr[crcBefore] ;/*
            mov     dword ptr[buffer], eax
            mov     eax, dword ptr[wantedCrc] ; Step 1
            mov     dword ptr[buffer+4], eax  ;*/

            mov     di, 4
    computeReverseLoop:
            mov     al, byte ptr[buffer+di+3] ;/*
            call   GetTableEntry              ; Step 2 */
            xor     dword ptr[buffer+di], eax ; Step 3
            xor     byte ptr[buffer+di-1], bl ; Step 4
            dec     di                        ;/*
            jnz    computeReverseLoop         ; Step 5 */

    Notes:
    -Registers eax, di bx are used

    Implementation of GetTableEntry

    crctable        dd 256 dup (?)       ;should be defined globally somewhere & initialized of course

            mov     bx, offset crctable-1
    getTableEntryLoop:
            add     bx, 4                ;points to (crctable-1)+k*4 (k:1..256)
            cmp     [bx], al             ;must always find the value somewhere
            jne     getTableEntryLoop

            sub     bx, 3
            mov     eax, [bx]
            sub     bx, offset crctable
            shr     bx, 2

            ret

    On return eax contains a table entry, bx contains the entry number.


    Outtro

      好了...你终于读到了本文的结尾.假如你认为从此不管对什么样的CRC保护都可以说bye
    bye了,那么你错了,不是的!很容易就可以写出对付破解CRC的代码的.想要成功的破解CRC
    你需要知道在一个保护中,到底使用的是那一种CRC算法,并且要知道CRC的具体的计算位置.
    比如说这里一种简单的对策就是使用2种不同的CRC算法,或者可以结合其他的数据保护算法
    共同使用.
      无论如何...我希望所有这里所介绍的内容都是受人关注的,并且我希望你(读者)可以很
    高兴的读着篇文章,就象我很高兴写一样.   


               

    翻译过程中难免有错误,不当之处,请见谅.              译者:  arbiter
                                                        2001-2-8 22:41
                                                       
                                                       
                                                       
    Fnx go out to the beta-testers Douby/DREAD and Knotty Dread for the good
    comments on my work which made it even better!

    For a sample CRC-32 correcting patcher program visit my webpages:
        http://surf.to/anarchriz  -> Programming -> Projects
    (it's still a preview but will give you a proof of my idea)

    For more info on DREAD visit http://dread99.cjb.net

    If you still have questions you can mail me at anarchriz@hotmail.com,
    or try the channels #DreaD, #Win32asm, #C.I.A and #Cracking4Newbies (in that
    order) on EFnet (on IRC).

    CYA ALL! - Anarchriz

    "The system makes its morons, then despises them for their ineptitude, and
    rewards its 'gifted few' for their rarity." - Colin Ward


    附录:

    CRC-16 Table

      00h   0000 C0C1 C181 0140 C301 03C0 0280 C241
      08h   C601 06C0 0780 C741 0500 C5C1 C481 0440
      10h   CC01 0CC0 0D80 CD41 0F00 CFC1 CE81 0E40
      18h   0A00 CAC1 CB81 0B40 C901 09C0 0880 C841

      20h   D801 18C0 1980 D941 1B00 DBC1 DA81 1A40
      28h   1E00 DEC1 DF81 1F40 DD01 1DC0 1C80 DC41
      30h   1400 D4C1 D581 1540 D701 17C0 1680 D641
      38h   D201 12C0 1380 D341 1100 D1C1 D081 1040

      40h   F001 30C0 3180 F141 3300 F3C1 F281 3240
      48h   3600 F6C1 F781 3740 F501 35C0 3480 F441
      50h   3C00 FCC1 FD81 3D40 FF01 3FC0 3E80 FE41
      58h   FA01 3AC0 3B80 FB41 3900 F9C1 F881 3840

      60h   2800 E8C1 E981 2940 EB01 2BC0 2A80 EA41
      68h   EE01 2EC0 2F80 EF41 2D00 EDC1 EC81 2C40
      70h   E401 24C0 2580 E541 2700 E7C1 E681 2640
      78h   2200 E2C1 E381 2340 E101 21C0 2080 E041

      80h   A001 60C0 6180 A141 6300 A3C1 A281 6240
      88h   6600 A6C1 A781 6740 A501 65C0 6480 A441
      90h   6C00 ACC1 AD81 6D40 AF01 6FC0 6E80 AE41
      98h   AA01 6AC0 6B80 AB41 6900 A9C1 A881 6840

      A0h   7800 B8C1 B981 7940 BB01 7BC0 7A80 BA41
      A8h   BE01 7EC0 7F80 BF41 7D00 BDC1 BC81 7C40
      B0h   B401 74C0 7580 B541 7700 B7C1 B681 7640
      B8h   7200 B2C1 B381 7340 B101 71C0 7080 B041

      C0h   5000 90C1 9181 5140 9301 53C0 5280 9241
      C8h   9601 56C0 5780 9741 5500 95C1 9481 5440
      D0h   9C01 5CC0 5D80 9D41 5F00 9FC1 9E81 5E40
      D8h   5A00 9AC1 9B81 5B40 9901 59C0 5880 9841

      E0h   8801 48C0 4980 8941 4B00 8BC1 8A81 4A40
      E8h   4E00 8EC1 8F81 4F40 8D01 4DC0 4C80 8C41
      F0h   4400 84C1 8581 4540 8701 47C0 4680 8641
      F8h   8201 42C0 4380 8341 4100 81C1 8081 4040


    CRC-32 Table

      00h   00000000 77073096 EE0E612C 990951BA
      04h   076DC419 706AF48F E963A535 9E6495A3
      08h   0EDB8832 79DCB8A4 E0D5E91E 97D2D988
      0Ch   09B64C2B 7EB17CBD E7B82D07 90BF1D91

      10h   1DB71064 6AB020F2 F3B97148 84BE41DE
      14h   1ADAD47D 6DDDE4EB F4D4B551 83D385C7
      18h   136C9856 646BA8C0 FD62F97A 8A65C9EC
      1Ch   14015C4F 63066CD9 FA0F3D63 8D080DF5

      20h   3B6E20C8 4C69105E D56041E4 A2677172
      24h   3C03E4D1 4B04D447 D20D85FD A50AB56B
      28h   35B5A8FA 42B2986C DBBBC9D6 ACBCF940
      2Ch   32D86CE3 45DF5C75 DCD60DCF ABD13D59

      30h   26D930AC 51DE003A C8D75180 BFD06116
      34h   21B4F4B5 56B3C423 CFBA9599 B8BDA50F
      38h   2802B89E 5F058808 C60CD9B2 B10BE924
      3Ch   2F6F7C87 58684C11 C1611DAB B6662D3D

      40h   76DC4190 01DB7106 98D220BC EFD5102A
      44h   71B18589 06B6B51F 9FBFE4A5 E8B8D433
      48h   7807C9A2 0F00F934 9609A88E E10E9818
      4Ch   7F6A0DBB 086D3D2D 91646C97 E6635C01

      50h   6B6B51F4 1C6C6162 856530D8 F262004E
      54h   6C0695ED 1B01A57B 8208F4C1 F50FC457
      58h   65B0D9C6 12B7E950 8BBEB8EA FCB9887C
      5Ch   62DD1DDF 15DA2D49 8CD37CF3 FBD44C65

      60h   4DB26158 3AB551CE A3BC0074 D4BB30E2
      64h   4ADFA541 3DD895D7 A4D1C46D D3D6F4FB
      68h   4369E96A 346ED9FC AD678846 DA60B8D0
      6Ch   44042D73 33031DE5 AA0A4C5F DD0D7CC9

      70h   5005713C 270241AA BE0B1010 C90C2086
      74h   5768B525 206F85B3 B966D409 CE61E49F
      78h   5EDEF90E 29D9C998 B0D09822 C7D7A8B4
      7Ch   59B33D17 2EB40D81 B7BD5C3B C0BA6CAD

      80h   EDB88320 9ABFB3B6 03B6E20C 74B1D29A
      84h   EAD54739 9DD277AF 04DB2615 73DC1683
      88h   E3630B12 94643B84 0D6D6A3E 7A6A5AA8
      8Ch   E40ECF0B 9309FF9D 0A00AE27 7D079EB1

      90h   F00F9344 8708A3D2 1E01F268 6906C2FE
      94h   F762575D 806567CB 196C3671 6E6B06E7
      98h   FED41B76 89D32BE0 10DA7A5A 67DD4ACC
      9Ch   F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5

      A0h   D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252
      A4h   D1BB67F1 A6BC5767 3FB506DD 48B2364B
      A8h   D80D2BDA AF0A1B4C 36034AF6 41047A60
      ACh   DF60EFC3 A867DF55 316E8EEF 4669BE79

      B0h   CB61B38C BC66831A 256FD2A0 5268E236
      B4h   CC0C7795 BB0B4703 220216B9 5505262F
      B8h   C5BA3BBE B2BD0B28 2BB45A92 5CB36A04
      BCh   C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D

      C0h   9B64C2B0 EC63F226 756AA39C 026D930A
      C4h   9C0906A9 EB0E363F 72076785 05005713
      C8h   95BF4A82 E2B87A14 7BB12BAE 0CB61B38
      CCh   92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21

      D0h   86D3D2D4 F1D4E242 68DDB3F8 1FDA836E
      D4h   81BE16CD F6B9265B 6FB077E1 18B74777
      D8h   88085AE6 FF0F6A70 66063BCA 11010B5C
      DCh   8F659EFF F862AE69 616BFFD3 166CCF45

      E0h   A00AE278 D70DD2EE 4E048354 3903B3C2
      E4h   A7672661 D06016F7 4969474D 3E6E77DB
      E8h   AED16A4A D9D65ADC 40DF0B66 37D83BF0
      ECh   A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9

      F0h   BDBDF21C CABAC28A 53B39330 24B4A3A6
      F4h   BAD03605 CDD70693 54DE5729 23D967BF
      F8h   B3667A2E C4614AB8 5D681B02 2A6F2B94
      FCh   B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D


    参考:

    > A painless guide to CRC error detection algorithm
      url: ftp://ftp.adelaide.edu.au/pub/rocksoft/crc_v3.txt
      (I bet this 'painless guide' is more painfull then my 'short' one ;)
    > I also used a random source of a CRC-32 algorithm to understand the algorithm
       better.
    > Link to crc calculation progs... hmmm search for 'CRC.ZIP' or 'CRC.EXE' or something
    alike at ftpsearch (http://ftpsearch.lycos.com?form=advanced)

    Copyright (c) 1998,1999 by Anarchriz
    (this is REALLY the last line :)

    转载于:https://www.cnblogs.com/MaxWoods/archive/2006/03/03/342298.html

    展开全文
  • ADSL通信系统中CRC原理及案例分析 绪论 循环冗余校验码(CRC)因编码简单且误判概率很低在通信系统中得到了广泛的应用循环冗余校验码的英文全称为Cyclic Redundancy Check缩写为CRC CRC是ADSL通信系统中关于误码率BER...
  • CRC原理详解算法原理查表法反向算法附录1:crc16校验表及用法 算法原理 Cyclic Redundancy Check循环冗余检验,是基于数据计算一组效验码,用于核对数据传输过程中是否被更改或传输错误。 假设数据传输过程中需要...
  • CRC原理和算法总结

    2010-11-02 12:41:10
    介绍了计算机网络中的CRC算发。并且针对学习CRC原理的一些个人总结。
  • 这篇文是在网上搜集的,特拿来与大家共享:我学习CRC32、CRC16、CRC原理和算法的总结(与WINRAR结果一致
  • CRC原理

    2013-01-28 16:28:52
    矛与盾的较量(2)——CRC原理篇 上一节我们介绍了花指令,不过花指令毕竟是一种很简单的东西,基本上入了门的Cracker都可以对付得了。所以,我们很有必要给自己的软件加上更好的保护。CRC校验就是其中的一种...
  • CRC原理简介

    千次阅读 2013-02-21 13:37:20
    在数据存储和数据通讯领域,为了保证数据的... 为了方便理解CRC原理, 我们这里先介绍一种简单的错误检测方法:校验和。  待发送的消息 : 6 23 4  带校验和的消息 : 6 23 4 33  接受端收到的消息 : 6 27 4 33
  • 蓝牙:CRC原理详解(附crc16校验代码)

    千次阅读 2019-03-19 19:54:01
    CRC原理详解(附crc16校验代码) 参考链接: https://www.cnblogs.com/esestt/archive/2007/08/09/848856.html CyclicRedundancyCheck循环冗余检验,是基于数据计算一组效验码,用于核对数据传输过程中是否被...
  • CRC原理简析 1. CRC校验原理 CRC校验原理根本思想就是先在要发送的帧后面附加一个数(这个就是用来校验的校验码,但要注意,这里的数也是二进制序列的,下同),生成一个新帧发送给接收端。当然,这个附加的数不是...
  • 我学习CRC32、CRC16、CRC原理和算法的总结(与WINRAR结果一致) wxleasyland(wxlwww@gmail.com) 2010年9月2日 比较愚钝,学了CRC校验好几天,很痛苦的过程,现终于有眉目了,总结一下。 国外版的“轻松无痛苦学
  • CRC原理总结

    2018-05-08 23:13:00
    1.CRC校验具体原理如下:  在要发送的数据帧后面附加一个数(这个就是用来校验的验证码,都为二进制序列),生成一个新帧发送给接受端。当然这个附加的数不能是随意的,它要使所生成的新帧与 发送端和接收端共同...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,821
精华内容 728
关键字:

crc原理