精华内容
下载资源
问答
  • 网站密码加密原理

    千次阅读 2011-12-22 22:50:30
    为了保证不管是真的钥匙,还是火柴棍,只要锁感觉爽,就一定要打开这个原则,锁是通过比较一串字节码来确定自己是否爽,这也很容易理解,真的钥匙会触到她的痒痒肉——李银河老师叫它G点——还不明白就问苍老湿。...

    网站密码加密原理(初级)

    ——致CSDN们

    我本来不想写这么初级的文章,但是发生了CSDN这当子事情之后,我还得真得写点入门级的文章,给你们科普科普。

    第一:什么是密码。

    通俗地讲,密码就是进入一道门的钥匙。而之所以我们能找到我们要进入的门,靠的是门牌号。那么门牌号就是用户名。不知道我这么讲,你们能不能听明白。钥匙就是带锯齿的小铁片,插到一个铁做的洞洞里,如果刚好吻合,旋转一下,就卡擦一声,打开了。简单吧。打开了门,就发生了人类说不完的故事。

    但是人类发明了钥匙和锁以来,从来没有破解不了的锁。这也算是锁的悲哀。

    通常我们从商店里买来的锁带有3把钥匙,而锁厂是不能留下一个备份的。可是,现在的情形不是这样子,CSDN给我们每人一把钥匙,并且很好心地为每个用户保存了一个钥匙的备份。遗憾的是,CSDN把一长串的钥匙搞丢了,而且每把钥匙上还有我们的住址门牌号。CSDN其实是锁,钥匙不能和锁放在一起,这是最基本的常识。锁只知道自己的洞洞的形状,但不应该知道钥匙的形状。锁当感觉有东西插入自己的身体的时候,不管这东西是真的钥匙,还是火柴杆,如果锁觉得爽,就卡的开了。原理就这么简单。为了保证不管是真的钥匙,还是火柴棍,只要锁感觉爽,就一定要打开这个原则,锁是通过比较一串字节码来确定自己是否爽,这也很容易理解,真的钥匙会触到她的痒痒肉——李银河老师叫它G点——还不明白就问苍老湿。这串字节码就是钥匙上的锯齿。钥匙上的锯齿理论上是毫无规则的。那么,比较这个字节码是否一致就是锁感知钥匙的过程。锁总要有凭据来验证钥匙对不对。对于CSDN的实现来讲,它们简单地复制了一份钥匙拴在锁上,然后把用户插进来的那个钥匙和自己的进行比较。这种做法是不对的,错就错在:锁如果丢了,连带钥匙也丢了。你见过谁家把锁和钥匙放一起的吗?

    第二:MD5。

    数学家提供给我们许多很巧妙的算法,用来解决钥匙齿的生成问题,它们都遵循下面4个定律:

    Rule-1)如果输入一堆东西,不管是什么,不管多少,总对应一个固定大小的输出(钥匙齿

    Rule-2)如果输入不同,那么输出就一定不同(或者在极小的概率内相同)。

    Rule-3)如果输入相同,那么输出就一定相同。

    Rule-4)如果输入有一丁点不同,比如输入2头驴,其中一头仅仅比另一头多了一根毛,那么输出就截然不同。

    例如:

      输入一本书--->MD5--->输出一串固定长度的数字,如132234532495435343243245

      输入一个太阳--->MD5--->输出一串固定长度的数字,如9384845739237439254357439

      输入一个银河系--->MD5--->输出一串固定长度的数字,如675454365464564354656334

    巧妙吧。但是从输出,无论如何我们也不能得到输入的是什么。

    其中一个算法就是MD5。MD5处理输入的过程有人叫加密,有人叫签名。不管怎么称呼,都是不能根据输出来破解出输入的。理论上的确存在输入一只狗和一只航天飞机,得到的输出结果确是一样的情形,这种特例就和你不小心用黄瓜打开了一把锁的概率差不多。

    考虑把输入当成密码的原文,而锁只保存MD5的输出结果,这样,用户在客户端页面输入密码之后,立即被MD5成固定长度的输出,然后把这个输出发送到CSDN那里,CSDN为每个用户保存了一个密码的MD5输出,通过比较这个输出,就知道是不是密码匹配了。如果CSDN保存的是MD5的输出。那么此次泄密的后果会小的多。因为我们无法从输出得到输入,也就无法知道密码。而存放明文密码是严厉禁止的。CSDN搞的跟“受害”者似的,其实“受害”的是用户。

    第三:仅仅保存密码的MD5的输出还是不够的。

    很多情况下,很多人设置的密码是相同的。那么相同的密码MD5处理得到的输出定长字符串就一定是一样的(Rule-3)。这给恶意的人猜中密码的机会。所以还需要加点佐料,让任何的输入都不一样,这个佐料我们称它为Salt——盐,不同的盐被加入到密码里搅拌,这样任何的输入就不同了。对于用户名——密码的情形,这个盐就是随机生成的字符串或数字而已。这样CSDN在用户的验证数据库中至少要保存密码加了盐之后的MD5值,当然也要保存盐值。用户登录的过程就变成:

    1)***用户输入用户名和验证码,向CSDN发送登录请求

    2)CSDN检查验证验,然后根据用户名到数据库里找到用户存储的盐返回给用户(验证码错误是不能返回盐的)

    3)用户输入密码,然后在网页客户端用javascript把盐撒到用户的密码上,搅拌,然后MD5,得到输出

    4)用户发送这个输出给CSDN——passport服务器

    5)服务器把用户提交的MD5输出直接和数据库中存储的输出值做是否相等比较,一致就通过,不一致就拒绝。

    ***之所以强调用户首先输入验证码,是防止模拟自动机自动登录。遗憾的是目前CSDN的登录流程仍然存在问题,验证码居然是输入密码之后才出现的。

    第四:光有盐还是不够的。

    盐是用来保证,即使有人把用户数据库的资料泄密了,别人也无法轻易猜测出密码来。没有盐的加入就会导致MD5值大量一致的情况。

    如果没有盐,当我恰好知道自己的密码的MD5恰好和她的密码的MD5值一样,而我的密码是iloveu,那么她的密码一定就是iloveu。有了盐,即使我知道她的盐,因为MD5出来的差异大不同(Rule-4),所以我也无法知道她的密码,即使我们的密码是一样的。盐是要确保每个用户都是不一样的。一般的随机算法或时间函数都能实现这一点。

    盐如果泄密了,黑客仍然能够通过穷举法得到我们的密码。方法是黑客使用与我们相同的加盐和搅拌方法,得到MD5值。所以就引入了一次一密的登录加密方法,一次一密不是我们每次都要改登录密码,而是我们每次登录之后,盐都是要重新计算,更新——我称这个是酱油。这给黑客破解我们的密码增加了相当大的难度。我怀疑CSDN没有引入这个酱油,最多是加了碘盐。

    其实,黑客不需要知道我们的密码,它们只需要截断消息,伪造session和消息,就能冒充我们。因此对于网上银行一类的网站,光有盐和酱油还是远远不够的。这就牵扯了ssl,证书,key,短信,token等等一堆的东西。网站不但要验证我们,我们也要验证网站:对于用户,你必须是我信赖的网站;对于网站,你必须是我的用户。

    第五:希望CSDN有盐。

    如果有空就把酱油也加进去吧。

    展开全文
  • 该图从上往为格雷码递增级数,每一级为一幅编码投影图案的黑白分布图样,从左至右,编码值从“0000000”按照格雷码编码依次递增至“1000000”。 投影图案设计 采用与投影图案 μ、ν 轴两个方向相同的两...

    格雷码+相移法既可以减少格雷码的编码位数,加快解码速度,也可以弥补单纯的相移法和格雷码法的对不连续位置难以重建的缺点。

    操作过程如下:

    采用格雷码与相移结合的时间编码方法,具体的编码方法为:首先向被测物投射一系列格雷码黑白条纹图案,其中具有相同编码
    的区域作为一个编码周期,然后再采用四步相移法,依次投射四幅相移图案,使得每个编码区域被进一步连续细分。但投射的格雷码图案和相移图案必须满足如下关系:格雷码图案的最小周期为相移图案周期的 4 倍,理论上格雷码周期边界与相移周期边界要严格对应。

     

    格雷码的编码算法为:

    1,由已有的前面的格雷码生成后面的格雷码

    假设已经生成了k位格雷码,那么k+1位格雷码的生成方式为

    (1) 按序在k位格雷码前插入一位0,生成一组编码

    (2)按逆序在k位格雷码前插入一位1,生成另外一组编码

    (3)两组编码合起来就是k+1位格雷码。

    如下例:

    已有2位格雷码:00, 01, 11, 10,要生成3位格雷码,采用此算法:

    (1)按序在各码前插入0,生成 000,001, 011,010;

    (2)按逆序在各码前插入1,生成 110,111, 101,100;

    (3)将两组编码组合起来:000, 001, 011, 010, 110, 111, 101, 100,为3位格雷码。

     

    另外一种算法与此算法类似,不同的是插入的位是在格雷码的后面:

    对于k位格雷码,在各格雷码后面分别插入0, 1 或 1, 0,生成两个编码,所有插入完成后组合起来的编码为k+1位格雷码。

    如已有2位格雷码:00,01,11,10,生成3位格雷码,采用此算法:

    (1)在00编码后面分别插入0,1,生成 000, 001;

    (2)在01编码后面分别插入1,0,生成 011, 010;

    (3)在11编码后面分别插入0,1,生成 110, 111;

    (4)在10编码后面分别插入1,0,生成 101,100;

    (5)将生成的编码组合起来:000, 001, 011, 010, 110, 111, 101, 100,为3位格雷码。

    对格雷码编码测试代码如下(有详细的注释):

    #include <iostream>
    #include <vector>
    #include <string>
    #include <time.h>
     
    void GrayCodeOne(int num);
    void GrayCodeTwo(int num);
     
    using namespace std;
     
    int main()
    {
    	int count;
    	cout << "Input Code Number:";  
    	cin >> count;//输入一个整形数
     
    	cout << "Produce Gray Code using method 1" << endl;
    	clock_t beginOne = clock();
    	GrayCodeOne(count);
    	clock_t endOne = clock();
    	cout << "Gray Code First Method using time: " << (endOne - beginOne) << endl;
     
    	cout << "Produce Gray Code using method 2" << endl;
    	clock_t beginTwo = clock();
    	GrayCodeTwo(count);
    	clock_t endTwo = clock();
    	cout << "Gray Code Second Method using time: " << (endTwo - beginTwo) << endl;
     
    	return 0;
    }
     
    // Method to produce gray code using method inserting 0 in front of old gray code by positive  
    //方法1中的已有的格雷码序列,正向前面一位加0,逆向前面一位加1
    // and inserting 1 in front of old gray code by nagative.
    void GrayCodeOne(int num)
    {
    	if (num < 1)
    	{
    		cout << "Error input Integer" << endl;
    		return;
    	}
     
    	vector<string> codeVec;  //定义一个字符串类型的向量 用来存放编码序列
     
    	int cIdx = 1;
    	for (; cIdx <= num; cIdx++)
    	{
                    //如果num=1,则for循环只有一次,codeVec大小就为2,里面放的是0,1
    		//开始向量为空,for循环的第一次,该向量大小一定小于2,直接存放0,1,
                    //存放一次结束后向量大小为2,则进入下面的else里
                    if (codeVec.size() < 2)  
    		{
    			codeVec.push_back("0");
    			codeVec.push_back("1");
    		}
    		else
    		{
                            //如果codeVec大小大于等于2个,则开始在,0和1上进行下面操作
    			vector<string> tranVec;  
    			tranVec.resize(2 * codeVec.size());  //定义一个中转向量,确定tranVec的大小为原有元素向量的2倍
    			int tranIdx = 0;
                            //迭代器对向量进行遍历
    			vector<string>::iterator codeIter = codeVec.begin();
    			for (; codeIter != codeVec.end(); codeIter++)
    			{
                                    //在正序输出的每个元素的前面加0
    				string str = "0";
    				str.append(*codeIter);
    				tranVec[tranIdx++] = str;//将加0后的元素重新存到转移向量
    			}
     
    			vector<string>::reverse_iterator rCodeIter = codeVec.rbegin();  //逆序迭代器,rbegin指向容器c的最后一个元素
    			for (; rCodeIter != codeVec.rend(); rCodeIter++)
    			{
                                    //在逆序输出的每个元素的前面加1
    				string str = "1";
    				str.append(*rCodeIter);
    				tranVec[tranIdx++] = str; 
    			}
     
    			codeVec.assign(tranVec.begin(), tranVec.end());
                            //将区间tranVec[first,last)的元素全部赋值到当前的codeVec容器中
                            //就是当前依序所编码的格雷码
    		}
    	}
     
    	//vector<string>::iterator vecIter = codeVec.begin();
    	//for (; vecIter != codeVec.end(); vecIter++)
    	//{
    	//	cout << *vecIter << endl;
    	//}
     
    	return;
    }
     
    // Method to produce gray code using method inserting 0/1 in the back of first gray code
    // then inserting 1/0 in the back of next gray code.
    void GrayCodeTwo(int num)
    {
    	if (num < 1)
    	{
    		cout << "Input error Integer" << endl;
    		return;
    	}
     
    	vector<string> codeVec;
     
    	int cIdx = 1;
    	for (; cIdx <= num; cIdx++)
    	{
    		if (codeVec.size() < 2)
    		{
    			codeVec.push_back("0");
    			codeVec.push_back("1");
    		}
    		else
    		{
    			vector<string> tranVec;
    			int tranIdx = 0;
    			int cIdx = codeVec.size();
     
    			tranVec.resize(2 * cIdx);
    			for (int vIdx = 0; vIdx < cIdx; vIdx++)
    			{
    				string str = codeVec[vIdx];
    				if (0 == (vIdx % 2))
    				{
    					string str0 = str;
    					str0.append("0");
    					tranVec[tranIdx++] = str0;
     
    					string str1 = str;
    					str1.append("1");
    					tranVec[tranIdx++] = str1;
    				}
    				else
    				{
    					string str0 = str;
    					str0.append("1");
    					tranVec[tranIdx++] = str0;
     
    					string str1 = str;
    					str1.append("0");
    					tranVec[tranIdx++] = str1;
    				}
    			}
     
    			codeVec.assign(tranVec.begin(), tranVec.end());
    		}
    	}
     
    	//vector<string>::iterator vecIter = codeVec.begin();
    	//for (; vecIter != codeVec.end(); vecIter++)
    	//{
    	//	cout << *vecIter << endl;
    	//}
     
    	return;
    }

    2、由二进制转换为格雷码

    这里的二进制数是整型数,直接对整型数进行操作获得格雷码。

    static unsigned int binaryToGray(unsigned int num) {
        return (num >> 1) ^ num;
    }

              unsigned int型数据到格雷码的转换,最高可转换32位自然二进制码

              int型数据到格雷码的转换,最高可转换31位自然二进制码。

     

    格雷码图案的生成:

    上面测试程序为一维格雷码的生成程序,下面为二维的格雷码图案的生成:

    格雷码图案的生成是在一维格雷生成的原理上将一位编码扩展到一列的编码。

    该图从上往下为格雷码递增级数,每一级为一幅编码投影图案的黑白分布图样,从左至右,编码值从“0000000”按照格雷码编码依次递增至“1000000”。

    投影图案设计

    采用与投影图案 μ、ν  轴两个方向相同的两个系列格雷码编码,其中每个方向最高级别格雷码宽度为 1 个像素,该设计方法具体分如下 2步:

    1)确定投影图案分辨率,假设水平 μ 方向上编码条纹设为 m 级,竖直 ν 方向上编码条纹设为 n 级,则投影图案在 μ 方向上像素个数为 2^m,ν 方向上像素个数为 2^n,即投影图案分辨率是 2^m×2^n。  
    2)投影图案分辨率确定后,使用格雷码编码方法,设计两个方向的编码投影图案,共生成 μ 方向上 m 张格雷码的编码图案,每张格雷码级别依次递增,和 ν 方向上 n 张格雷码的编码图案,每张格雷码级别依次递增。

    设计代码(有注释)如下:

    //设置的水平方向的条纹数是10,垂直方向的条纹数是6
    static unsigned int Nhorz = 10;
    static unsigned int Nvert = 6;
    
    #ifndef log2f
    #define log2f(x) (log(x)/log(2.0))
    #endif
    
    using namespace std;
    
    /*
     * The purpose of this function is to convert an unsigned
     * binary number to reflected binary Gray code.
     *
     * The operator >> is shift right. The operator ^ is exclusive or.
     * Source: http://en.wikipedia.org/wiki/Gray_code
     */
    static unsigned int binaryToGray(unsigned int num) {
        return (num >> 1) ^ num;
    }
    
    /*
     * From Wikipedia: http://en.wikipedia.org/wiki/Gray_code
     * The purpose of this function is to convert a reflected binary
     * Gray code number to a binary number.
    */
    static unsigned grayToBinary(unsigned num, unsigned numBits)
    {
        for (unsigned shift = 1; shift < numBits; shift <<= 1){
            num ^= num >> shift;
        }
        return num;
    }
    
    /*
     * Function takes the decimal number
     * Function takes the Nth bit (1 to 31)
     * Return the value of Nth bit from decimal
     * Source: http://icfun.blogspot.com/2009/04/get-n-th-bit-value-of-any-integer.html
     */
    static int get_bit(int decimal, int N){
    
        // Shifting the 1 for N-1 bits   
        int constant = 1 << (N-1);   //将00000001左移N-1位
    
        // If the bit is set, return 1
        if( decimal & constant ){
            return 1;
        }
    
        // If the bit is not set, return 0
        return 0;
    }
    
    static inline int powi(int num, unsigned int exponent){
        // NOT EQUIVALENT TO pow()
        if(exponent == 0)
            return 1;
    
        float res = num;
        for(unsigned int i=0; i<exponent-1; i++)
            res *= num;
    
        return res;
    }
    
    // Encoder   参数为:行 列 方向
    EncoderGrayCode::EncoderGrayCode(unsigned int _screenCols, unsigned int _screenRows, CodecDir _dir) : Encoder(_screenCols, _screenRows, _dir){
    
        N = 2;  //初始值为2
    
        // Set total pattern number
        if(dir & CodecDirHorizontal)
            this->N += Nhorz;         //如果编码为水平方向,N为2+Nhorz  N=12
    
        if(dir & CodecDirVertical)    //如果编码为水平方向,N为2+Nvert  N=8
            this->N += Nvert;
    
        // Encode every pixel column     //编码每一像素列  NbitsHor=10
        int NbitsHorz = ceilf(log2f((float)screenCols));  //先将列数转变为浮点数,再将列数取log2f运算,将float类型的小数去掉,然后进一,
    
        // Number of vertical encoding patterns  同上 针对每一行  9.58去掉小数进1为10,即NbitsVert=10
        int NbitsVert = ceilf(log2f((float)screenRows));
    
    	//patterns的前两个元素为patternOn和patternOff
        cv::Mat patternOn(1, 1, CV_8UC3, cv::Scalar(0));  //定义一个1x1的patternOn矩阵
        patternOn.at<cv::Vec3b>(0,0) = cv::Vec3b(255, 255, 255);   //矩阵的(0,0)点定义为(255, 255, 255)白色
        patterns.push_back(patternOn); 
    
        cv::Mat patternOff(1, 1, CV_8UC3, cv::Scalar(0));  //定义一个1x1的patternOff矩阵
        patterns.push_back(patternOff);
    
    
        if(dir & CodecDirHorizontal){
            // Precompute horizontally encoding patterns
            for(unsigned int p=0; p<Nhorz; p++){
                cv::Mat patternP(1, screenCols, CV_8UC3);   //定义一个1行xscreenCols列的patternP矩阵
    
                // Loop through columns in first row       从第一行遍历所有列
                for(unsigned int j=0; j<screenCols; j++){
                    unsigned int jGray = binaryToGray(j);   //将二进制转换为格雷码
    
                    // Amplitude of channels   设置每个通道的灰度值
                    float amp = get_bit(jGray, NbitsHorz-p);
                    patternP.at<cv::Vec3b>(0,j) = cv::Vec3b(255.0*amp,255.0*amp,255.0*amp);
                }
                patterns.push_back(patternP);
            }
        }
        if(dir & CodecDirVertical){
            // Precompute vertical encoding patterns     
            for(unsigned int p=0; p<Nvert; p++){
                cv::Mat patternP(screenRows, 1, CV_8UC3);    //定义一个screenRows行x1列的patternP矩阵
    
                // Loop through rows in first column       从第一列遍历所有行
                for(unsigned int i=0; i<screenRows; i++){
    
                    unsigned int iGray = binaryToGray(i);
    
                    // Amplitude of channels
                    float amp = get_bit(iGray, NbitsVert-p);
                    patternP.at<cv::Vec3b>(i,0) = cv::Vec3b(255.0*amp,255.0*amp,255.0*amp);
                }
                patterns.push_back(patternP);
            }
        }
    
        #if 0
            for(unsigned int i=0; i<patterns.size(); i++){
                std::stringstream fileNameStream;
                fileNameStream << "pattern_" << std::setw(2) << std::setfill('0') << i << ".bmp";
                cv::imwrite(fileNameStream.str(), patterns[i]);
            }
    
        #endif
    }

    在上述条纹生成的代码中,以横向的条纹图案,重要的步骤为

    (1)设置两个一个像素大小的矩阵

    (2)先分配一个空矩阵用来存放生成的pattern,从01两位格雷码开始开始

             内层for循环是对第一行的所有像素点按照编码的格雷码值进行灰度赋值,即

             如果P=0,则为第1张条纹图,用第一行每一列数(从0到1023)转换的格雷码值的 第10 位值去填充该列像素灰度值,

             这时,Mat矩阵中存放的是水平排布(竖直条纹)条纹第一行的一维行条纹。

            需要说明的是,这里的生成思路很巧妙

           首先是直接对图像的宽度进行格雷码转换,可以直接对任意的图像尺寸进行编码;

           同时对格雷码值取位操作,所取的位数正好分割了尺寸,每一行被分割成N=1024/2^(NbitsHorz-P)等份,每一份像素个数为

           1024/N。

           如果要生成一张图像,则需要将第一行的条纹进行复制,opencv里的repeat函数,矩阵拷贝的时候指定按x/y方向重复

          这里将第一行的条纹在Y方向按图像的行数重复,在X方向上重复为1次(不做操作),即可得到完整的一张格雷码图案。

    (3)二进制数转为一维格雷码代码

    (4)这个函数主要是将整型数所转换的一维格雷码的第N位对应的0,1状态取出

    最后生成了横向条纹竖直分布的格雷码并存在了patterns中,纵向分布的类似。

    Matlab代码:

    function [P,offset] = graycode(width,height)
    
    % % Define height and width of screen.
    % width  = 1024;
    % height = 768;
    
    % Generate Gray codes for vertical and horizontal stripe patterns.
    % See: http://en.wikipedia.org/wiki/Gray_code
    P = cell(2,1); %胞元
    offset = zeros(2,1);  
    for j = 1:2
       
       % Allocate storage for Gray code stripe pattern.
       if j == 1
          N = ceil(log2(width));
          offset(j) = floor((2^N-width)/2);
       else
          N = ceil(log2(height));
          offset(j) = floor((2^N-height)/2);
       end
       P{j} = zeros(height,width,N,'uint8');
       
       % Generate N-bit Gray code sequence.
       B = zeros(2^N,N,'uint8');
       B_char = dec2bin(0:2^N-1);  %十进制转二进制
       for i = 1:N
          B(:,i) = str2num(B_char(:,i));
       end
       G = zeros(2^N,N,'uint8');
       G(:,1) = B(:,1);
       for i = 2:N
          G(:,i) = xor(B(:,i-1),B(:,i));
       end
       
       % Store Gray code stripe pattern.  B = repmat(A,m,n)  B 由 m×n 个 A 平铺而成
       if j ==1 
          for i = 1:N
             P{j}(:,:,i) = repmat(G((1:width)+offset(j),i)',height,1);
          end
       else
          for i = 1:N
             P{j}(:,:,i) = repmat(G((1:height)+offset(j),i),1,width);
          end
       end
       
    end

    matlab程序的思路:

    此时,格雷码图案完全生成,根据格雷码图案生成相移条纹图案,格雷码的最小周期为T=log2(width),相移条纹的周期为T/4即可,具体生成过程在另一篇博文详细说明,这里不再重复。

    格雷码编码程序实例3(C++),对应于解码程序。

    // Generate Gray codes.     格雷码在生成时 通过sl_scan_cols和sl_scan_rows的状态来判断生成行还是列条纹图案 
    
    int generateGrayCodes(int width, int height,     //以1024 * 768为例
    					  IplImage**& gray_codes,    
    					  int& n_cols, int& n_rows,
    					  int& col_shift, int& row_shift, 
    					  bool sl_scan_cols, bool sl_scan_rows){
    
    	// Determine number of required codes and row/column offsets.
    	if(sl_scan_cols){                            //判断是生成哪种条纹
    		n_cols = (int)ceil(log2(width));         //n_cols  格雷码的张数,即格雷码的最小宽度   n_cols=10 ;ceil是求大于该数的整数
    		col_shift = (int)floor((pow(2.0,n_cols)-width)/2);       //列偏移量  比宽度多出来的?  col_shift=0;foolr是求小于该数的整数
    	}
    	else{
    		n_cols = 0;
    		col_shift = 0;
    	}
    	if(sl_scan_rows){
    		n_rows = (int)ceil(log2(height));      //ceil(9.58) = 10 =n_rows
    		row_shift = (int)floor((pow(2.0,n_rows)-height)/2);      //行偏移量 row_shift = floor((1024-768)/2)=128
    	}
    	else{
    		n_rows = 0;
    		row_shift = 0;
    	}	
    
    	// Allocate Gray codes.   分配内存  
    	gray_codes = new IplImage* [n_cols+n_rows+1];   //为什么要加1?加一张白色图案
    
    	for(int i=0; i<(n_cols+n_rows+1); i++)  //i<21
    		gray_codes[i] = cvCreateImage(cvSize(width,height), IPL_DEPTH_8U, 1);   //创建n_cols+n_rows+1 = 21张空图像内存,其中第一张为 全白图案
    
    	int step = gray_codes[0]->widthStep/sizeof(uchar);  //widthStep=图像的宽度*通道数,即每一行需要的内存长度;step即每一行有多少个uchar类型的数据
    
    	// Define first code as a white image.
    	cvSet(gray_codes[0], cvScalar(255));
    
    	// Define Gray codes for projector columns.    投影仪的列         三层循环!!!
    	for(int c=0; c<width; c++){
    		for(int i=0; i<n_cols; i++){     //i从0到9共循环10次,一行的每一像素都要循环10次,每循环一次就是对每一张图像的操作
    			uchar* data = (uchar*)gray_codes[i+1]->imageData;       //data指针 指向格雷码图像的数据的地址
    
    			if(i>0)  //从第二张图案开始  data[c]是一维数组,只能放一个值,应该是0或者1
    				data[c] = (((c+col_shift) >> (n_cols-i-1)) & 1)^(((c+col_shift) >> (n_cols-i)) & 1);//先右移后与   i=1时,c分为3个部分,0-255,255-767,768-1023,编码分别为0,1,0 
    			else
    				data[c] = (((c+col_shift) >> (n_cols-i-1)) & 1);    //如果i=0时,c<=511,data[c]=0,c>511,data[c]=1
    
    			//col_shift的作用:如果投影仪尺寸为912*1140,n_cols为10,则在编码时是以1024位来进行编码的,以c=0为例,会导致0-511=0,512-912=1,左右不等长
    			//所以仍需要以512为中心,通过将0-912后移56变为56-968,这样512则变为了中心,左右编码位数相等。
    			//像素值全部乘以255
    			data[c] *= 255;  
    
    			for(int r=1; r<height; r++)     //得到一行的编码后执行的平铺操作 
    				data[r*step+c] = data[c];	//r*step为每一行所占的内存
    		}
    	}
    
    	// Define Gray codes for projector rows.       投影仪的行
    	for(int r=0; r<height; r++){
    		for(int i=0; i<n_rows; i++){
    			uchar* data = (uchar*)gray_codes[i+n_cols+1]->imageData; //格雷码的行列图案数据内存地址是连续的  
    			if(i>0)
    				data[r*step] = (((r+row_shift) >> (n_rows-i-1)) & 1)^(((r+row_shift) >> (n_rows-i)) & 1);
    			else
    				data[r*step] = (((r+row_shift) >> (n_rows-i-1)) & 1);
    			data[r*step] *= 255;                   //data[r*step] = data[r*step]*255
    			for(int c=1; c<width; c++)
    				data[r*step+c] = data[r*step];	
    		}
    	}
    
    	// Return without errors.
    	return 0;
    }

    程序解读:

    1,两个bool类型状态变量:sl_scan_cols,sl_scan_rows,判断生成行还是列条纹图案。

    2,根据投影仪的投影图案的尺寸(以912*1140为例)来计算横向和纵向条纹图案的位数和生成的张数以及怕行和列的偏移量。

    3,为格雷码的存储分配内存,并在第一张单独存储全白图案,其余格格雷图案内存连续存储。

    4,通过3层循环来实现格雷码图案的生成,重点在每一行(列)格雷码编码的生成:

            i=0时,编码方式为(((c+col_shift) >> (n_cols-i-1)) & 1);       

            当i从1变到n_cols时,n_cols-i-1和n_cols-i的值为:

           该句代码则是对每一个像素点进行编码,并通过异或来获得编码值0或1,推导过程如下:

    该程序与之前的不同之处在于:

    (1)加入了行和列的偏移量,在编码时是:

    col_shift的作用:如果投影仪尺寸为912*1140,n_cols为10,则在编码时是以1024位来进行编码的,以c=0为例,会导致0-511=0,512-912=1,左右不等长,所以仍需要以512为中心,通过将0-912后移56变为56-968,这样512则变为了中心,左右编码位数相等。

    (2)数据全部在内存中操作,可以减小内存消耗,加快运算速度。

     

    如果有疑问或者见到其他的不同的格雷码编码程序,请留言或QQ联系博主交流学习(857467352)。

    展开全文
  • 本文主要介绍了卡尔曼滤波器的使用原理,给出了matlab代码,并在STM32F407平台对卡尔曼滤波器进行了验证,传感器为MPU6050与DPS310,测试结果令人满意,速度与高度无累积误差。...其中X(k)为当前状态,X(k+1)为一...

    本文主要介绍了卡尔曼滤波器的使用原理,给出了matlab代码,并在STM32F407平台对卡尔曼滤波器进行了验证,传感器为MPU6050与DPS310,测试结果令人满意,速度与高度无累积误差。


    系统状态方程

    在开始讲卡尔曼滤波器之前需要先提一下状态方程。因为卡尔曼的计算公式是建立在状态方程上的,所以我们需要先写出系统的状态方程。离散状态方程为:

    其中X(k)为当前状态,X(k+1)为下一时刻状态,Φ为转移矩阵,B为控制矩阵,u为控制量,Г为噪声矩阵,W为系统噪声,Y为输出量,H为输出矩阵,V为观测噪声。简单来说就是通过这一时刻已知的状态、控制量及系统噪声可以求出此刻的能观测到的输出以及下一时刻的状态。

    那很么又是状态呢?对于我们要分析的系统来说,加速度、速度、以及高度就是系统的状态,也就是说公式中的X(k)就是包含加速度、速度、以及高度的向量。

    同理

    而状态转移矩阵Φ是表述下一时刻状态与此刻状态关系的矩阵,在本系统中我们能够非常清楚得列出他们的关系,假设我们采样周期T比较短,可以近似认为加速度a几乎不变(关于为什么a认为不变可参考卡尔曼滤波器阶次问题),则

    将上面几个等式写成矩阵形式则为

    由此我们可以得到转移矩阵Φ就是

    对于我们要分析的系统,没有控制量,不考虑其他系统噪声的情况下,后面两项可以直接拿掉,状态方程简化为

    状态方程第一个式子分析完了接下来分析下第二个式子。

    我目前现有的传感器为MPU6050以及气压计DPS310,能够得到的物理量为加速度及气压,也就是说我能直接观测到的物理量是加速度和气压,那么状态方程的观测输出Y就是加速度和气压

    那么如何从系统状态X(k)得到Y(k)呢?这个时候查阅网上资料发现在海拔较低且变化范围几百米以内时,气压与高度成线性关系,其系数为0.09,气压每变化ΔP时,高度变化0.09*ΔP,由此我们可以得到关系

    其中P0为参考平面的气压,也就是h=0时的气压。然后我们就可以写成矩阵形式

    得到H和V

    我们得到系统的离散状态方程后,可以开始卡尔曼滤波了。


    卡尔曼滤波

    虽说叫卡尔曼滤波,但是它不止有滤波功能,还能对多个传感器得到的数据进行数据融合,因此应用非常广泛。这里就不细讲晦涩难懂的卡尔曼具体原理和推导了,只给出线性卡尔曼公式和每个公式的作用。

    首先来感性得认识一下两个直接影响卡尔曼滤波效果的参数测量误差R和过程误差Q。测量误差R是反映传感器得到信息质量的优劣,传感器得到信号质量越差,则R应越大,从而有更强的滤波效果。虽然R越大滤波效果更强,但是响应速度会变慢,因此R不宜过大。一般可以通过传感器的数据手册直接得到其测量误差,也可以一个个值试,直到得到想要的滤波效果。而过程误差Q反映的是在测量过程中受到别的环境因素影响的大小,如气压计易受到风、温度的干扰之类的,当Q为0时,得到的滤波效果会非常平滑,但是会存在累积误差之类的缺点,当Q较大时,滤波效果会变差,一般Q取一个较小值比较合适,比如0.0001。

    一次卡尔曼滤波可分为以下五个过程

    1. 通过上一次状态的最优估计X(k-1)得到本次状态的预测\hat{X}(k)
    2. 计算本次协方差矩阵预测\hat{P}(k)
    3. 计算滤波增益矩阵K(k)
    4. 根据实际观测输出Y(k)、增益矩阵K(k)对本次预测的状态\hat{X}(k)进行修正得到本次状态最优估计X(k)
    5. 更新协方差矩阵P(k)

    第一步:计算本次状态预测

    本次状态预测就是将上次最优估计得到的状态代入状态方程即可得到。

    第二步:计算本次协方差矩阵预测

    我们的状态有h、v、a三个变量,故协方差矩阵P为三阶矩阵,P(k-1)为上次协方差矩阵,初始协方差矩阵P(0)可设为对角阵,对角上每个p值为三个变量对应的初始协方差,一般取1-10,初始协方差矩阵对后面没有影响。本系统中,Г为单位矩阵,Q为3阶对角阵,对角上的每个q值为三个变量对应的过程误差。

    第三步:计算滤波增益矩阵

    由上一步得到的\hat{P}(k)、输出矩阵H以及初始化时设定好的观测噪声矩阵R计算得到本次滤波增益矩阵K(k)。

    第四步:计算本次状态最优估计

    对第一步得到预测状态进行修正得到本次最优估计。其中Y(k)为本次传感器得到的实际测量值。

    第五步:更新协方差矩阵

    根据前几步得到的增益矩阵K与协方差估计矩阵\hat{P}(k)得到本次协方差矩阵。


    Matlab仿真

    DataUp.mat中包含MPU6050采集到的三轴加速度以及气压计DPS310采集到的气压,数据从电梯轿厢中测得,由于电梯开门后轿厢内气压值有变化,因此开门时气压值有些变化,造成速度计算值有些偏差。

    clear;
    load('DataUp.mat');
    len = length(z);
    % 减去重力加速度后的加速度
    a = (z-1)*9.8;
    
    % 参数设置
    sam_frq = 1000;
    T = 1/sam_frq;
    k = 0.09;%压高系数
    
    % 观测误差R、过程误差Q
    R = 0.5*eye(2);
    R(1,1) = 150;    
    R(2,2) = 0.5;
    Q = 0.0001*eye(3);
    Q(2,2) = 0.00001;
    
    % 状态方程矩阵
    F = [1,T,0.5*T*T;0,1,T;0,0,1];
    H = [-1/k,0,0;0,0,1];
    V = [pre(1);0];
    
    % 数据初始化
    Xkf = zeros(3,len);
    Z = zeros(2,len);
    Z(1,:) = pre;
    Z(2,:) = a;
    Xkf(:,1) = [0;0;0];
    P0 = eye(3);
    P0(1,1) = 10;
    
    % 卡尔曼滤波
    for i = 2:len
        Xn = F*Xkf(:,i-1);
        P1 = F*P0*F'+ Q;
        K = (P1*H')/(H*P1*H'+R);
        Xkf(:,i) = Xn + K*(Z(:,i)-H*Xn-V);
        P0 = (eye(3)-K*H)*P1;
    end
    
    % 显示图像
    figure;
    plot((pre(1)-pre)*0.09);
    hold on;
    plot(Xkf(1,:));
    hold on;
    plot(Xkf(2,:));
    hold on;
    plot(Xkf(3,:));
    hold on;
    plot(a);

    以下是仿真结果

    气压计滤波效果:

    速度计算效果:

    加速度滤波效果:

    STM32F407平台验证

    实际传感器都有线性偏差,尤其是MPU6050的线性偏差也就是三个轴加速度的真实值a=kx+b,通过最小二乘法确定k和b。获得加速度计以多个姿态静置时(确保只受到重力加速度)的加速度作为拟合数据后,按照Matlab中的lsqcurvefit,非线性拟合中给出拟合方法进行拟合得到最终线性补偿系数k、b。

    在实际应用中系数k对最终结果影响不大,但我们仍然需要得到系数b,因为加速度计的固定偏差会影响误差的累计。b的获取可通过静置时加速度相对于重力加速的的偏移得到。

    先读出MPU6050的三轴加速度原始值,即三个short类型数据。经过处理后得到除重力加速度的垂直方向加速度。MPU6050水平放置,则z轴加速度可近似为垂直方向的加速度。

    //加速度补偿值计算
    for(int i=0; i<100; i++){
    	MPU_Get_Accelerometer(&aacx,&aacy,&aacz);
    	b = Acc_Comp(aacz/16384.0*G - G);
    	delay_ms(5);
    }
    

    其中补偿值计算方式为

    /**
      * @func	加速度计补偿值计算
      * @param	a			减去重力加速度后的加速度
      * 
      * @ret	补偿值
      * @use	循环使用该函数20次以上才能得到比较精确的补偿值
      */
    float Acc_Comp(float a)
    {
    	static float b = 0;
    	b = b*0.95 + a*0.05;
    	return -b;
    }

    主循环

     while(1)
    {
        // 读取加速度
        MPU_Get_Accelerometer(&aacx,&aacy,&aacz);	
    
        // 计算加速度
        accexx = aacx/16384.0*G;
        acceyy = aacy/16384.0*G;
        accezz = aacz/16384.0*G - G + b;
    	
        // 读取气压
        Dps301_read_press(&pressure, cal_coe);
    	
        // 卡尔曼滤波
        Kalman_Fil_Calc(&KF, accezz, pressure);
    		
        // 获得滤波后的高度、速度和加速度
        h = KF.X[0][0];
        v = KF.X[1][0];
        a = KF.X[2][0];
    
        LED0=!LED0;
        delay_ms(10);
    } 	

    最终通过jscope观察h、v、a的波形输出

    完整matlab代码及keil工程

    卡尔曼滤波器计算加速度、速度和高度的Matlab仿真和STM32验证

    关于评论中提到的速度为0的BUG解决方法是在“kalman_rank2.h”文件中Period的宏定义前加个强制类型转换即可,新上传的资源已修复该bug

    另外在实际应用中采样频率对测量误差R的影响很大,修改完采样频率后需要修改卡尔曼滤波器初始化时传入的参数pre_r和acc_r,具体值以实际效果好为准,不用考虑范围,如采样频率为5Hz时,可修改为pre_r=0.01,acc_r=0.0003

     

    展开全文
  • 芯片加密解密

    千次阅读 2014-05-13 16:45:16
    若没有显微镜,则采用将芯片的不同部分暴露到紫外光下并观察结果的方式进行简单的搜索。操作时应用不透明的纸片覆盖芯片以保护 程序存储器 不被紫外擦除。将保护熔丝暴露在紫外光下5~10分钟就能破坏掉保护位的...

    基本概述编辑

    单片机(MCU)一般都有内部EEPROM/FLASH供用户存放程序和工作数据。为了防止未经授权访问或拷贝单片机的机内程序,大部分单片机都带有加密锁定位或者加密字节,以保护片内程序。如果在编程加密锁定位被使能(锁定),就无法用普通编程器直接读取单片机内的程序,这就叫单片机加密或芯片加密。单片机攻击者借助专用设备或者自制设备,利用单片机芯片设计上的漏洞或软件缺陷,通过多种技术手段,就可以从芯片中提取关键信息,获取单片机内程序这就叫芯片破解。

    2芯片加密狗的破解编辑

    硬件克隆复制主要是针对国产芯片的加密狗,因为国产加密狗公司一般没有核心加密芯片的制造能力,因此有些使用了市场上通用的芯片,破解者分析出芯片电路以及芯片里写的内容后,就可以立刻复制或克隆一个完全相同的加密狗。不过国外的加密狗就无法使用这种方法,国外加密狗硬件使用的是安全性很好的自己研制开发的芯片,通常很难进行复制,而且现在国内加密狗也在使用进口的智能卡芯片,因此这种硬件克隆的解密方法用处越来越少。
    对于Debug调试破解,由于软件的复杂度越来越高,编译器产生的代码也越来越多,通过反汇编等方法跟踪调式破解的复杂度已经变得越来越高,破解成本也越来越高,目前已经很少有人愿意花费大量精力进行如此复杂的破解,除非被破解的软件具有极高的价值。
    目前加密锁(加密狗)的解密破解工作主要集中在应用程序与加密动态库之间的通讯拦截。这种方法成本较低,也易于实现,对待以单片机等芯片为核心的加密锁加密狗)具有不错的解密效果。
    由于加密锁(加密狗)的应用程序接口(API)基本上都是公开的,因此从网上可以很容易下载到加密狗的编程接口API、用户手册、和其它相关资料,还可以了解加密狗技术的最新进展。

    3芯片破解方法编辑

    (1)软件攻击

    该技术通常使用处理器通信接口并利用协议、加密算法或这些算法中的安全漏洞来进行攻击。软件攻击取得成功的一个典型事例是对早期ATMEL AT89C 系列单片机的攻击。攻击者利用了该系列单片机擦除操作时序设计上的漏洞,使用自编程序在擦除加密锁定位后,停止下一步擦除片内程序存储器数据的操作,从而使加过密的单片机变成没加密的单片机,然后利用编程器读出片内程序。
    目前在其他加密方法的基础上,可以研究出一些设备,配合一定的软件,来做软件攻击。
    近期国内出现了了一种51芯片破解设备(成都一位高手搞出来的),这种解密器主要针对SyncMos. Winbond,在生产工艺上的漏洞,利用某些编程器定位插字节,通过一定的方法查找芯片中是否有连续空位,也就是说查找芯片中连续的FF FF字节,插入的字节能够执行把片内的程序送到片外的指令,然后用解密的设备进行截获,这样芯片内部的程序就被解密完成了。

    (2) 电子探测攻击

    该技术通常以高时间分辨率来监控处理器在正常操作时所有电源和接口连接的模拟特性,并通过监控它的电磁辐射特性来实施攻击。因为单片机是一个活动的电子器件,当它执行不同的指令时,对应的电源功率消耗也相应变化。这样通过使用特殊的电子测量仪器和数学统计方法分析和检测这些变化,即可获取单片机中的特定关键信息。
    目前RF编程器可以直接读出老的型号的加密MCU中的程序,就是采用这个原理。

    (3)过错产生技术

    该技术使用异常工作条件来使处理器出错,然后提供额外的访问来进行攻击。使用最广泛的过错产生攻击手段包括电压冲击和时钟冲击。低电压和高电压攻击可用来禁止保护电路工作或强制处理器执行错误操作。时钟瞬态跳变也许会复位保护电路而不会破坏受保护信息。电源和时钟瞬态跳变可以在某些处理器中影响单条指令的解码和执行。

    (4)探针技术

    该技术是直接暴露芯片内部连线,然后观察、操控、干扰单片机以达到攻击目的。
    为了方便起见,人们将以上四种攻击技术分成两类,一类是侵入型攻击(物理攻击),这类攻击需要破坏封装,然后借助半导体测试设备、显微镜和微定位器,在专门的实验室花上几小时甚至几周时间才能完成。所有的微探针技术都属于侵入型攻击。另外三种方法属于非侵入型攻击,被攻击的单片机不会被物理损坏。在某些场合非侵入型攻击是特别危险的,这是因为非侵入型攻击所需设备通常可以自制和升级,因此非常廉价。
    大部分非侵入型攻击需要攻击者具备良好的处理器知识和软件知识。与之相反,侵入型的探针攻击则不需要太多的初始知识,而且通常可用一整套相似的技术对付宽范围的产品。因此,对单片机的攻击往往从侵入型的反向工程开始,积累的经验有助于开发更加廉价和快速的非侵入型攻击技术。

    4侵入型芯片破解过程编辑

    侵入型攻击的第一步是揭去芯片封装(简称“开盖”有时候称“开封”,英文为“DECAP”,decapsulation)。有两种方法可以达到这一目的:第一种是完全溶解掉芯片封装,暴露金属连线。第二种是只移掉硅核上面的塑料封装。第一种方法需要将芯片绑定到测试夹具上,借助绑定台来操作。第二种方法除了需要具备攻击者一定的知识和必要的技能外,还需要个人的智慧和耐心,但操作起来相对比较方便,完全家庭中操作。
    芯片上面的塑料可以用小刀揭开,芯片周围的环氧树脂可以用浓硝酸腐蚀掉。热的浓硝酸会溶解掉芯片封装而不会影响芯片及连线。该过程一般在非常干燥的条件下进行,因为水的存在可能会侵蚀已暴露的铝线连接 (这就可能造成解密失败)。
    接着在超声池里先用丙酮清洗该芯片以除去残余硝酸,并浸泡。
    最后一步是寻找保护熔丝的位置并将保护熔丝暴露在紫外光下。一般用一台放大倍数至少100倍的显微镜,从编程电压输入脚的连线跟踪进去,来寻找保护熔丝。若没有显微镜,则采用将芯片的不同部分暴露到紫外光下并观察结果的方式进行简单的搜索。操作时应用不透明的纸片覆盖芯片以保护程序存储器不被紫外光擦除。将保护熔丝暴露在紫外光下5~10分钟就能破坏掉保护位的保护作用,之后,使用简单的编程器就可直接读出程序存储器的内容。
    对于使用了防护层来保护EEPROM单元的单片机来说,使用紫外光复位保护电路是不可行的。对于这种类型的单片机,一般使用微探针技术来读取存储器内容。在芯片封装打开后,将芯片置于显微镜下就能够很容易的找到从存储器连到电路其它部分的数据总线。由于某种原因,芯片锁定位在编程模式下并不锁定对存储器的访问。利用这一缺陷将探针放在数据线的上面就能读到所有想要的数据。在编程模式下,重启读过程并连接探针到另外的数据线上就可以读出程序和数据存储器中的所有信息。
    还有一种可能的攻击手段是借助显微镜和激光切割机等设备来寻找保护熔丝,从而寻查和这部分电路相联系的所有信号线。由于设计有缺陷,因此,只要切断从保护熔丝到其它电路的某一根信号线(或切割掉整个加密电路)或连接1~3根金线(通常称FIB:focused ion beam),就能禁止整个保护功能,这样,使用简单的编程器就能直接读出程序存储器的内容。
    虽然大多数普通单片机都具有熔丝烧断保护单片机内代码的功能,但由于通用低档的单片机并非定位于制作安全类产品,因此,它们往往没有提供有针对性的防范措施且安全级别较低。加上单片机应用场合广泛,销售量大,厂商间委托加工与技术转让频繁,大量技术资料外泻,使得利用该类芯片的设计漏洞和厂商的测试接口,并通过修改熔丝保护位等侵入型攻击或非侵入型攻击手段来读取单片机的内部程序变得比较容易。
    目前国内比较有名的芯片破解公司有沪生电子,余洋电子,星辰单片机,恒丰单片机和龙人科技等。

    5应对芯片破解的建议编辑

    任何一款单片机从理论上讲,攻击者均可利用足够的投资和时间使用以上方法来攻破。这是系统设计者应该始终牢记的基本原则。因此,作为电子产品的设计工程师非常有必要了解当前单片机攻击的最新技术,做到知己知彼,心中有数,才能有效防止自己花费大量金钱和时间辛辛苦苦设计出来的产品被人家一夜之间仿冒的事情发生。我们根据沪生的解密实践提出下面建议:
    (1)在选定加密芯片前,要充分调研,了解芯片破解技术的新进展,包括哪些单片机是已经确认可以破解的。尽量不选用已可破解或同系列、同型号的芯片选择采用新工艺、新结构、上市时间较短的单片机,如可以使用ATMEGA88/ATMEGA88V,这种国内目前破解的费用一需要6K左右,另外目前相对难解密的有ST12系列,DSPPIC等;其他也可以和CPLD结合加密,这样解密费用很高,解密一般的CPLD也要1万左右。
    (2)尽量不要选用MCS51系列单片机,因为该单片机在国内的普及程度最高,被研究得也最透。
    (3)产品的原创者,一般具有产量大的特点,所以可选用比较生僻、偏冷门的单片机来加大仿冒者采购的难度,选用一些生僻的单片机,比如ATTINY2313,AT89C51RD2,AT89C51RC2,motorola单片机等比较难解密的芯片,目前国内会开发使用熟悉motorola单片机的人很少,所以破解的费用也相当高,从3000~3万左右。
    (4)在设计成本许可的条件下,应选用具有硬件自毁功能的智能卡芯片,以有效对付物理攻击;另外程序设计的时候,加入时间到计时功能,比如使用到1年,自动停止所有功能的运行,这样会增加破解者的成本。
    (5)如果条件许可,可采用两片不同型号单片机互为备份,相互验证,从而增加破解成本。
    (6)打磨掉芯片型号等信息或者重新印上其它的型号,以假乱真(注意,反面有LOGO的也要抹掉,很多芯片,解密者可以从反面判断出型号,比如51,WINBOND,MDT等)。
    (7)可以利用单片机未公开,未被利用的标志位或单元,作为软件标志位。
    (8)利用MCS-51中A5指令加密,其实世界上所有资料,包括英文资料都没有讲这条指令,其实这是很好的加密指令,A5功能是二字节空操作指令加密方法在A5后加一个二字节或三字节操作码,因为所有反汇编软件都不会反汇编A5指令,造成正常程序反汇编乱套,执行程序无问题仿制者就不能改变你的源程序。
    (9)你应在程序区写上你的大名单位开发时间及仿制必究的说法,以备获得法律保护;另外写上你的大名的时候,可以是随机的,也就是说,采用某种算法,外部不同条件下,你的名字不同,比如wwwhusooncom1011、wwwhusooncn1012等,这样比较难反汇编修改。
    (10)采用高档的编程器,烧断内部的部分管脚,还可以采用自制的设备烧断金线,这个目前国内几乎不能解密,即使解密,也需要上万的费用,需要多个母片。
    (11)采用保密硅胶(环氧树脂灌封胶)封住整个电路板,PCB上多一些没有用途的焊盘,在硅胶中还可以掺杂一些没有用途的元件,同时把MCU周围电路的电子元件尽量抹掉型号。
    (12)对SyncMos,Winbond单片机,将把要烧录的文件转成HEX文件,这样烧录到芯片内部的程序空位自动添00,如果你习惯BIN文件,也可以用编程器把空白区域中的FF改成00,这样一般解密器也就找不到芯片中的空位,也就无法执行以后的解密操作。
    当然,要想从根本上防止单片机被解密,那是不可能的,加密技术不断发展,解密技术也不断发展,现在不管哪个单片机,只要有人肯出钱去做,基本都可以做出来,只不过代价高低和周期长短的问题,编程者还可以从法律的途径对自己的开发作出保护(比如专利)。
    展开全文
  • 全网最硬核 JVM TLAB 分析(单篇版不包含额外菜) 全网最硬核 JVM TLAB 分析 1. 内存分配思想引入 全网最硬核 JVM TLAB 分析 2. TLAB生命周期与带来的问题思考 全网最硬核 JVM TLAB 分析 3. JVM EMA期望算法与TLAB...
  • 速度计和陀螺仪原理

    万次阅读 多人点赞 2015-10-31 15:21:48
    本帖翻译自IMU(速度计和陀螺仪设备)在嵌入式应用中使用的指南。 这篇文章主要介绍速度计和陀螺仪的数学模型和基本算法,以及如何融合这两者,侧重算法、思想的讨论 介绍 本指南旨在向兴趣者介绍惯性MEMS(微...
  • 无线WEP、WPA加密与解密原理

    万次阅读 2015-12-24 16:05:40
    目前情况:  WEP的破解为利用加密体制缺陷,通过收集足够的数据包,使用分析密算法还原出密码。  WPA目前没有加密体制的缺陷可被利用,破解WPA密码使用的是常规的字典攻击法。  所以在破解方式上WEP和WPA有很大...
  • 三轴速度传感器原理及应用

    万次阅读 2017-06-25 22:10:44
    三轴速度传感器原理 MEMS换能器(Transducer)可分为传感器(Sensor)和致动器(Actuator)两类。其中传感器会接受外界的传递的物理性输入,通过感测器转换为电子信号,再最终转换为可用的信息,如速度传感器、...
  • PCB加工文件—Gerber文件的导出

    万次阅读 2019-04-27 13:50:24
    2)出现如下对话框,确认红色选中区域内的格式设置,点击确定 3)生成钻孔文件XXXX.TXT(XXXX为PCB命名) 五.绘文件输出 1.在PCB工程文件中包含了PCB加工需要的绘文件。至此便可将这些文件打包发给PCB厂家。...
  • python减乘除运算

    千次阅读 2020-02-20 22:52:14
    使用python做的一个数据运算程序,计算100以内的减乘除,数据随机产生 小游戏使用tkinter拓展包,文本的创建和随机数的生成,随机运算符的选择。 运行结果: 确认答案后,将运算数据存入.text文件: 在这里插入...
  • 推荐   ♥各位如果想要交流的话,可以加下QQ交流群:974178910,里面有各种你想要的学习资料。♥   ♥欢迎大家关注公众号【不温卜火】,关注公众号即可以提前阅读又可以获取各种干货哦,同时公众号每满1024及...
  • 什么是量子加密(一)

    千次阅读 2019-08-30 23:24:28
    同样可以把偏振旋转45°,这样45°方向的偏振就代表0,135°方向振动的光子代表1,不妨称之为B方案。到此,我们已经可以通过A、B两套不同的方案发射0或者1这样的数值了。当然,接收方自然也就可以利用A、B两套...
  • 密码和Java中的解密之基础部分

    千次阅读 2012-10-15 14:43:12
    流密码对数据流进行,一次一位或一个字节。 分组密码可以用来创建流密码,反之亦然。对单条信息加密,应使用分组密码。对一个信息流,最好使用流密码,如一个socket。 对称加密算法的强度主要由密钥的长度决定的...
  • 华为命令模块信息

    千次阅读 2018-06-11 15:37:58
    作为一名网络工程师,模块在日常工作中可谓是处处可见。有时,我们需要知道交换机上光模块的信息,来处理一些问题。那么除了查看模块上标签信息,我们还可以进入交换机系统,通过命令来获取更详细的内容。 ...
  • 公钥密码学中的素数以及对称加密

    万次阅读 2014-08-31 10:34:16
     到此为止,我们已经知道如何生成一个有限的集合来方便计算机进行任意的运算,但是这还不够,因为我们仅仅定义了一些运算法则以及一个有限的集合,单位元我们还没有,对于单位元而言,它可以作为集合内任意元素...
  • 课题十二 数控车削加工工艺分析

    千次阅读 2008-01-17 16:23:00
    确定几何角度困难或无法确定(如尖形车刀加工接近于半个凹圆弧的轮廓等)时,则应考虑选择其它类型车刀后,再确定其几何角度。 (2)圆弧形车刀的几何参数 ①圆弧形车刀的选用 圆弧形车刀具有宽刃切...
  • 联想7400一体机粉后粉盒重置归零方法  关掉机器 ————开机的同时按住功能按扭不松手开机————进入维修模式--——-——翻到84功能...《PROCES CHECK》————用键找到 TONER CART RESET -——-按确定
  • 速度计和陀螺仪区别

    千次阅读 2020-01-11 17:23:55
    一言以蔽之,速度计在较长时间的测量值(确定飞机航向)是正确的,而在较短时间内由于信号噪声的存在,而有误差。陀螺仪在较短时间内则比较准确而较长时间则会有与漂移而存有误差。因此,需要两者(相互调整)来...
  • 第一节 加工中心数控系统简介

    千次阅读 2008-01-17 15:35:00
    (3)确认急停按钮为急停状态,按操纵台右侧绿色开启按钮,这时CNC通电显示器亮,系统启动,进入图形用户界面屏幕. (4)合上外部设备空气压缩机电源开关,空压机启动送气到规定压力. (5)释放急停按钮,...
  • 树莓派基础实验31:MPU6050陀螺仪速度传感器实验

    千次阅读 多人点赞 2020-11-06 09:31:55
       它集成了3轴MEMS陀螺仪,3轴MEMS速度计,以及一个可扩展的数字运动处理器 DMP( DigitalMotion Processor),可用I2C接口连接一个第三方的数字传感器,比如磁力计。扩展之后就可以通过其 I2C或SPI接口输出一...
  • 【转】Android 速度传感器 (G-Sensor)

    千次阅读 2010-05-24 22:50:00
    http://blog.csdn.net/stevenliyong/archive/2009/09/13/4547568.aspx Android 速度传感器 (G-Sensor) 收藏 Android 速度传感器的类型是 Sensor.TYPE_ACCELEROMETER 通过 android.hardware.SensorEvent 返回...
  • 就写文字而言,不V不是我最欣赏的,但就卖书而言,微博上无人能出其右,第一屌师@杜子建 见了不V,也得温良谦恭让,这不是好男不跟女斗,是实力不如人。 有人问:不V在方韩大战期间一直就是韩黑,和方舟子
  • JavaScript语句后应该分号么?

    千次阅读 2012-11-23 20:52:36
    这是一个老生常谈的问题了。我之前就曾经写过一篇...于是我在问题http://www.zhihu.com/question/20298345,花了整整一天时间写了以下的回答。 重新发在blog上,主要是因为此文过长,作为知乎的答案或许应该精简
  • } else if(S2==0)//判断是否按 清零 { delayms(50); if(S2==0) //再次确定 { TR0=0; num=num+100; } while(!S2); //消抖 delayms(50); while(!S2); //跳出所在的第一个while循环 break; } else if(S3==0)//判断...
  • POST电自检及BIOS常见故障

    千次阅读 2014-01-30 10:31:16
    内并没有设定快速电自检的话,那么开机就会执行内存的测试,如果你不想等待,可按 ESC 键跳过或到 BIOS 内开启 Quick Power On Self Test 。        HARDDISK initializing 【 Please wait ...
  • LCD 调试总结(ZZ实践)

    千次阅读 2010-05-28 11:29:00
     在确认VPW,VBP,VFP,HPW,HBP,HFP的设置已经符合LCD规格要求后,如果屏幕的显示还在抖动的话,不妨将输出的时钟信号频率降低,有可能解决该问题。      19.一些英文的缩写    VPW: Vsync Pulse Width  VBP: ...
  • 因为当计步器在脚上时,运动过程中脚抬起和落是最明显的,即速度传感器的z轴是记录人体前进步数 的主要根据。 图2显示了与一名跑步者的竖向、前向和侧向速度相对应的x、y和z轴测量结果的典型图样。无论...
  • 今天研究了UGUI上面Image的扫效果,我是参考了下面这篇文章做的,然后自己了一些控制和判断,让扫看起来更加合理 原文地址:点击打开链接 我在他的基础上,了一个倾斜度参数,可以控制扫斜度,另外,...
  • 当输入端电信号时发光器发出光线,受光器接受光线之后就产生光电流,从输出端流出,从而实现了“电--电”转换。以为媒介把输入端信号耦合到输出端的光电耦合器。 完成了大功率开关电源主回路设计,该电路采用...
  • 因为小孩快6岁了,但是就玩,10以内加法都搞不清,不能忍啊,很多比他小的都会,然后开始是弄个加法表给他背,一点用都没,坐那玩,然后用java弄个程序随机出一些题打印出来给他做,有点用,但是还是不太理想,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 76,878
精华内容 30,751
关键字:

如何确定下加光