不灭的焱

革命尚未成功,同志仍须努力

作者:php-note.com  发布于:2019-08-08 18:17  分类:编程基础/Web安全  编辑

CRC是通信领域中用于校验数据传输正确性的最常用机制,也是Hash算法的一个典型应用,Hash一般翻译为“散列”,也可直接音译为“哈希”,就是把任意长度的输入(又叫做预映射,pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是散列值的空间通常远小于输入空间,不同的输入可能会散列成相同的输出,而不可能从散列值唯一的确定输入值。

 

关于CRC校验:(16位二进制数),CRC码由发送设备计算,放置于所发送信息帧的尾部。接收信息的设备重新计算所接收信息(除RCR部分)的CRC,比较计算得到的

 

CRC是否与接收到的CRC相符,如果两者不相符,则认为数据传输出错,如果相符说明数据传输正确。

 

(1)预1个16位的寄存器为0xFFFF,称此寄存器为CRC寄存器

(2)把分i一个8位二进制数据(即通信信息帧的第一个字节)与16位的CRC寄存器的低8位相异或,把结果存放于CRC寄存器

(3)把CRC寄存器的内容右移一位,用0填补最高位,并检查右移后的移出位。

(4)如果移出位为0,重复(3),如果移出位为1,CRC寄存器与0xA001进行异或

(5)重复(3)(4),直到右移8次,这样整个8位数据就全部进行了处理

(6)重复(2)到(5),进行通信信息帧下一个字节的处理

(7)将该通信信息帧所有字节按上述步骤计算完成后,得到的16位CRC寄存器的高、低位已经交换

(8)最后得到的CRC寄存器内容即为CRC码

 

程序代码如下:

#include <stdio.h>

unsigned short modbus_crc(unsigned char* p,int len) {
    unsigned short ret=0xFFFF;
    int i=0;
    int k=0;
 
    for(i=0;i<len;++i){
        ret^=p[i];
        for(k=0;k<8;++k){
            ret=(ret&0x01)?((ret>>1)^0xA001):(ret>>1);
        }
    }
    ret=((ret&0xFFFF)<<8) | ((ret&0xFFFF)>>8);
    return ret;
}
 
int main() {
    unsigned char input[]={0x01,0x04,0x0D,0x28,0x00,0x02,0x01,0x02,0x00,0x53,0x00,0x01};
    unsigned short out=0;
    int i=0;
    int len=sizeof(input);
 
    out=modbus_crc(input,sizeof(input));
    printf("Input:");
    for(i=0;i<len;i++) {
        printf(" 0x%.2X",input[i]);
    }
    printf("\nModbus CRC is: 0x%.4X\n",out);
    return 0;
}

常见的 Hash 算法有:MAC, CRC, MD5/MD4, SHA 等。

PHP版本

/**
 * 16位CRC散列算法(返回 十进制 的字符串形式)
 *
 * @param $string
 * @return int
 */
public function php_crc16($string) {
	$crc = 0xFFFF;
	for ($x = 0; $x < strlen($string); $x++) {
		$crc = $crc ^ ord($string[$x]);
		for ($y = 0; $y < 8; $y++) {
			if (($crc & 0x0001) == 0x0001) {
				$crc = (($crc >> 1) ^ 0xA001);
			} else {
				$crc = $crc >> 1;
			}
		}
	}
	return $crc;
}

举例:

$crc = php_crc16("我是 温建宝");     // 十进制 字符串
$bin = decbin($crc);            // 二进制 字符串
$hex = dechex($crc);            // 16进制 字符串

echo '十进制:', $crc, "\r\n";
echo '二进制:' . $bin, "\r\n";
echo '16进制:' . $hex, "\r\n";

输出:

十进制:56480
二进制:1101110010100000
16进制:dca0