返回列表 发帖

实用的几种单片机加密技术

供大家参考:在这里不讲加密算法,只讨论结合软硬件的加密方法,总结了一下大该有以下几种:

一、加密方法

     1、烧断数据总线。这个方法我想应不错,但应有损坏的风险,听说也能**。
     2、芯片打磨改型,这个方法有一定作用,改了型号能误导,但同时也增加成本,解密者一般也
            能分析出来。
     3、用不合格的单片机的的存储器:这个方法听起来不错,值得一试。很多单片机有这种情况,有
            的是小容量改为大容量来用,**者应很难发现。例:8031/8052 单片机就是8731/8752掩模
            产品中的不合格产品,内部可能有ROM。可把8031/8052 当8751/8752 来用.但使用时要测试
            可靠。
      4、其他还有添加外部硬件电路的加密方法。但那样增加成本,效果不一定好。
      5、软件加密,是一些防止别人读懂程序的方法,单一的这种方法不能防止别人全盘复制。须配合
            其他的加密方法。
      6、通过序列号加密,
            这个方法当你的产品是连接PC时或网络,我想是一个比较理想的方法。原理跟电话产品防伪
            标志相近。就是在你的单片机中生成一个唯一的随机长序列号,并加入复杂的特种算法,或加
            入你们重新编码的企业信息在里面,每个芯片内不同,复制者只能复制到一个序列号。这个方
            法不能防止复制,但能发现复制品,并可在升级或在网络状态控制它或让他自毁。如果产品不
            联机或不可升级,则这个方法完全无效,只能是在上法院时可当作证据,因为内含特种算法破
            解者是无法知道的。
      7、通过单片机唯一的特性标识(不可修改)进行加密
            这个方法最好,能很好的防止复制。但大多单片机没有唯一标识。STC单片机里面含唯一标识,
            但本人没用过,下次一定要研究使用一下。理论上只要含唯一标识是单片机都可实现,ATMEL
            AVR系列单片大部分型号有RC校正字节(几十个芯片才有一个相同,并且不可修改)能实现这
            个理想功能,可做到即使芯片内程序被读出也无法直接在另一个同型号的单片机上正常运行。并
           且如果用这个唯一标识来生成含有加密算法的序列号,结合第6种方法,哪应是最理想的加密方
            法。

以上方法应都是一种加密的思路,各种方法可接合着用,6、7两种方法是本人认为比较合适,实现起
来比较容易的方法。后面将重点介绍两种加密方式的实现方法。
二、序列号加密实现方法

    1、原理

          就是在存储器某个区块放入一个唯一的序列号(长一点,无规律),每个芯片不同。原理跟电话
           产品防伪标志相近

                                --------------                    ---------------------------------------------------------
                                |    PC机 | <------------>|        带自定义算法序列号单片机系统     |
                                --------------                   ---------------------------------------------------------

控制方法:
         1、PC根据传回来的序列号根据算法判断是否合法,合法就运行,不合法处理它。当然,如果是
               **的序列号,可自毁。
         2、单片机内部的序列号经加密算法处理,单片机系统同样要防止软件被更改,可在单片机内部加
               入CRC等数据校验。一般情况下,序列号如果不合算法,单片机系统应让程序运行出错,这样
               **者一般不会去修改序列号,如果修改了也没关系,因为PC还能判断是否合法。
         3、序列号传送时可采用双向加密算法认证,相当于银卡的数据交换方式。
传送过程:
         PC发送随机SEED数据---->单片机系统跟据随机SEED算出加密的序列号----->C根据算法判断序
         列号是否合法这样在序列号的传送过程中,数据每次不同,解密者无法看到序列号的明码。这样
         PC软件他同样不容易更改。

        注意:加密算法可以很简单理解一个为异或,当然算法由你自已随意定,反正目的不能上别人一看
                    软件就懂。**者只是**一个产品只能得到一个序列号,即使序列号是明码,他也只能知
                   道是一个。如他随便修改一个序列号一般情况就不符合算法,除非他看懂你的软件算法,我
                   想这是一般解密者最不愿意做的事情。
单片机系统的量产:
                   产生这样的序列号,单片机系统如何生成?如果用手工一个个去计算调入,得重新编译是不
                  可能的事情。如果编写一个软件生成数据放入到HEX文件中,那样不说工作量好大,编程时
                  还必须一次次装入HEX文件,量产同样无法完成。这个可于MiniPro TL866 编程器完成。

              编程器的介绍可查看:http://www.qcfdlt.com
        TL866编程器有个自动编号功能,可利用DLL动态库调用实现任意的序列号.如何使用DLL设定序列
号,可参考编程器安装包内的DLL实例,内有详细说明。后面讲的用单片机唯一的特性标识进行加密也
用到DLL调用,基本大同小异。
        经过这样的加密,达到一个目的,就是解密者必须修改你的软件后才能放心使用,但是每次升级同
样会受你控制,好像微软随时可以黑你屏一样的道理,否则可能随时会被你宰了!!但这种方法只适合
连接PC或网络的系统。

三、用单片机唯一的特征标识进行加密

加密原理:
              单片机必须有唯一标志,单片机程序内只要判断是否是这个标志,就可防止程序直接复制使
              用。理论上可以做到很难**,本人认为是最有效又实用的方法。

达到目的: 解密者最不愿意做的事让他必须做。
          一般大部分搞单片机解密的都是暴力**,因为这个最容易,只要牚握技巧,有设备,工艺熟练就可了,不用太强的专业知识。如果复制后,程序无法运行,那就蒙了,因为这个时候就要去看汇编语言了,我想信有很多汇编高手,能很容易**。但我也同时认为,怎么多型号的单片机,汇编指令差别好大,每一种单片机的汇编都很熟的人应不多了。所以这会大大增加**难度。如果一个加密设计好的软件,跟据单片机的唯一特征字来加密,有时他可能不得不看懂里面的加密算法。这样加密目的就达到了。
开发人员需要做的事:
          有了唯一特征字,并不是加密万无一失了。如果你只是在程序中只用一条语句判断,是不是这个芯片的特征字,则程序被读出后,解密者只要简单的修改程序,直接跳过判断语句。可能只要几分钟就解决了。所以在程序要加入加密算法,尽量不用IF判断语句。可用子程序调用地址来参与特征字的加密运算等等方法,如何防反汇编及修改软件等不在这里讨论,网上可以找到有关这方面的很多资料。

           下面讨论如何用TL866编程器实现,单片机用特征字进行加密的量产操作:

        芯片进行加密量产操作同样碰到到一个问题,如果加密算法比较复杂,每个芯片在代码中需要更改的数据较多,用手工的方法是无法实现量产操作编程的,TL866编程器很好的解决了这个问题,下面用ATMEGA8为例,说明TL866编程器如果进行加密量产编程操作。TL866编程器程序安装包(V3.01以上)中有该实例的全部动态库函数及单片机C源代码,稍作修改就可用到你的产品中。
下载地址:http://www.qcfdlt.cn//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//    ATMEGA8加密编程实例
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

1、ATMEGA8 实例原理图

2、加密测试方法

        测试步骤:
                        1.  选择ATMEAG8芯片
                        2.  调入OBJ录中的HEX数据文件到缓存CODE区
                              配置信息设定,默认不更改。
3.  设定DLL加密算法
                     这个过程同自定义编号算法
                      方法:<主菜单>--<操作>--<自动编号设定>
                      在对话框内设定自定义算法Get_M8_LED_DATA.dll,并选中允许自动编号后保存

4.在主程序左下角选择<允许自动编号>,编程芯片。
(配置位选择不进行物理加密,否则后面读不出这个芯片的内容进行复制)

5.按原理图连接电路,通电测试,LED有规律闪动正常。

6.读取刚编程芯片内的程序,把这个程序复制到另一个芯片中,运行测试结果。
(复制芯片时,不可调用DLL算法,否则芯片中是按这个芯片RC值得出的数据)
此时LED,无规律闪动。测试完成。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//ICCAVR C源程序说明
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
本程序在ATMEGA8上 用ICCAVR 6.31A测试通过
***************************************************************
程序功能:
        正常情问况下,程序按数组表是的顺序点亮LED,LED有规律地循环闪动。当程序从正常芯片中被复制到另一个ATMEGA8芯片上时你可看到程序工作异常,LED闪动混乱。
目的:本程序只是演示,如何使用TL866编程器对AVR单片机量产加密.原理是各芯片的RC校正字节不同,进行软件加密。使即使芯片被**,它的程序被读出, 也无法直接使用 .本加密只是用了一个数组异或,你可用任何你能想到的方法进行加密,并可在原代码中加入程序CRC校验,防止原代码修改等等方法。
单片机溶丝位设定: 默认
**************************************************************/


/*定义LED按一定正常顺序显示的数组

1.      table1 table2这两个数组定位到固定的地址, 是为了编程器可以保证访问到这些数据位置在没有加密的情况下,table1这个数组是一个亮灯的明码,这种情况下程序复制到任何一个芯片上都可正常运行,这里演示的加密主要是准对这个数组进行算法变换
2.      table2是一个任意值的数组,

*/
#pragma abs_address:0x800
//正常的亮灯顺序,//在具体加密中你可把它当成--子程序入口表
编程器编时里会自动放入反向的加密数据
const char table1[] =
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/

};
定义一个这个表是为了给数组绝对定位的空间,这个表中的值无意义
const char table2[] =
{
0xA5, 0x00, 0xE6, 0xBF, 0x46, 0x70, 0xE3, 0x2A, 0x22, 0xBE, 0x90, 0x9C, 0xA1, 0x05, 0xAE, 0xA5,
0xA0, 0xFE, 0x24, 0x5D, 0x66, 0x22, 0x97, 0xA7, 0xD9, 0x0F, 0x3C, 0xB7, 0xF1, 0x2D, 0xEA, 0xD2,
0x6E, 0x91, 0x74, 0x55, 0xC6, 0x22, 0x35, 0xF9, 0x62, 0xFA, 0xEC, 0x55, 0x09, 0x0F, 0xCB, 0xA5,
0x9D, 0x12, 0xCA, 0xC9, 0x05, 0x97, 0x41, 0x91, 0x6A, 0x77, 0xB6, 0x37, 0xA6, 0x72, 0xF3, 0x2D,
0xF9, 0x19, 0x5C, 0x19, 0xFE, 0xEC, 0x80, 0x1F, 0xDE, 0xBF, 0xEE, 0x5E, 0xC4, 0x62, 0x4A, 0xBD,
0x91, 0x80, 0xA0, 0xE7, 0xD0, 0xC8, 0xF7, 0x94, 0xEC, 0x4B, 0x2A, 0x0B, 0xA0, 0x25, 0xF3, 0xE4,
0xB0, 0x60, 0x49, 0x13, 0xD7, 0x15, 0xEC, 0x22, 0x00, 0xD4, 0x3E, 0xBE, 0xB9, 0x46, 0x54, 0x75,
0xE4, 0x11, 0x4D, 0xBE, 0xAF, 0xFC, 0xE2, 0x3A, 0xC7, 0x54, 0x3F, 0x39, 0xC9, 0x8E, 0x11, 0x80,
0xFA, 0x2D, 0xE1, 0x4A, 0x37, 0xE7, 0xA0, 0x8C, 0x2F, 0x03, 0x83, 0x7D, 0xCF, 0x05, 0x11, 0x56,
0xFE, 0x8D, 0x7A, 0x56, 0x8B, 0x7D, 0x2A, 0x0A, 0x63, 0x5A, 0x9E, 0xCB, 0x08, 0xF5, 0x76, 0x88,
0x3E, 0x4A, 0xCC, 0xC6, 0x08, 0xA8, 0xC5, 0xE4, 0xD2, 0x12, 0x65, 0xA4, 0xF0, 0xE6, 0xA8, 0xE7,
0x47, 0xBC, 0xCE, 0xB8, 0x4A, 0x91, 0xF6, 0x8C, 0x29, 0x25, 0xEE, 0xC8, 0x44, 0xA1, 0x4A, 0x84,
0xE6, 0x7D, 0xB3, 0x8F, 0x30, 0xA1, 0x82, 0xB3, 0x53, 0xCA, 0x8E, 0x3A, 0x03, 0x30, 0x41, 0xB0,
0x28, 0x66, 0xF2, 0xEB, 0xD6, 0x81, 0x6E, 0x49, 0x7F, 0x7C, 0xD8, 0x39, 0x67, 0xDB, 0xB4, 0xFD,
0x59, 0x8F, 0x3E, 0xAE, 0x99, 0x1A, 0x00, 0x80, 0x19, 0xF3, 0xA3, 0x47, 0xEF, 0x2C, 0x06, 0x3A,
0x07, 0x52, 0x8D, 0xF7, 0x15, 0x95, 0xBC, 0xC8, 0xCE, 0x28, 0x04, 0x25, 0x58, 0xEB, 0xDC, 0x7A
};
#pragma end_abs_address
/*延时程序*/
void Delay(void)
{
unsigned int i;
for(i=0;i<0x1000;i++);
}
/********************************************************************************
* Main 主程序
* 从是可以看出,本程序中没有IF语句,解密只能修改OSCCAL的值或修改数据表
* 如果你在程序中,加入程序代码校验,则必须先取消校验,才能**。相信你可加入更多的
* 方法来防止解密者修改源程序
*********************************************************************************/
void main()
{
unsigned char i,TempChar;
/*PORTB接LED灯*/
DDRB = 0xFF; /* PORTB引脚全为输出*/
PORTB = 0x00; /* 输出低电平 */
i=0;
while(1)
{
//----------------------------------------------------------
/*TempChar=table1;/*不加密时的输出方法*/
//------------------------------------------------------------------
/*加密时的输出方法*/
//-------------------------------------------------------------------
/*异或的开始位置与OSCCAL有关,这样OSCCAL变动数据会全乱*/
TempChar=table2[(i+OSCCAL)&0xFF]^table1;
TempChar^=OSCCAL;
/*这时的输出是OSCCAL寄存器与数据表中的值异或后的结果
默认情况OSCCAL的值就是ATMEGA8,默认1M的校正字节*/
/*这样二步运算结果,当然必须要做到,PORTB最后的结果和明码状态一样
也就是说,OSCCAL参与运算后的结果必须最后得出明码值,这样LED才正常.
这就要求编程器能在编程根据芯片的RC校正值自动生成table1[]及table2[]
表,使得最后运行的结果为正确的明码,*/
/*可以想像,因为OSCCAL值每个芯片不同,table1[]及table2[]表的数据放到
另一个芯片里面。最后运行的结果一定的错误的,达到加密的目的*/
//------------------------------------------------------------------------------
i++;
PORTB=TempChar;/*输出*/
Delay();
}

}


/*下一步我们来看看TL866编程器在编程芯片时需要做的:

第1步:读取RC校正字节值
第2步:随机生成一个table2的数据表,这是为迷惑**者的,table2每个芯片不同。
第3步: 根据RC值,Tables2表,明码表,---->反向计算出Table1表的数据
使单片机运算时能根据RC、Table1、Tables2表的数据刚好得到明码表。
第4步:将计算出的table1[],和前面随机生成的table2[]放入缓存写入到芯片中。

具体可查看安装目录内对应DLL原代码

通过以上工作后,即使芯片内程序被读出,放入到另一个芯片中程序也无法正常工作。
这里只是通过一个简单的示例,说明TL866编程器如何与单片机软件配合起来,进行
产品的量产过程。
实际使用的加密,应加入更多的加密算法,反跟踪,及原代码校验防止程序修改等等,
使**者必须认真看懂你的原代码,才能进行程序修改**。

*/

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//       DLL动态库函数说明
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


//定义一个正常的LED亮灯顺序表
//在具体加密中你可把把它放子程序入口表
const unsigned char table1[] =
{
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/
0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, /*1个灯亮走动*/

};
//定义一个随机表,这个表每个芯片不同随机生成
unsigned char table2[] =
{
0xA5, 0x00, 0xE6, 0xBF, 0x46, 0x70, 0xE3, 0x2A, 0x22, 0xBE, 0x90, 0x9C, 0xA1, 0x05, 0xAE, 0xA5,
0xA0, 0xFE, 0x24, 0x5D, 0x66, 0x22, 0x97, 0xA7, 0xD9, 0x0F, 0x3C, 0xB7, 0xF1, 0x2D, 0xEA, 0xD2,
0x6E, 0x91, 0x74, 0x55, 0xC6, 0x22, 0x35, 0xF9, 0x62, 0xFA, 0xEC, 0x55, 0x09, 0x0F, 0xCB, 0xA5,
0x9D, 0x12, 0xCA, 0xC9, 0x05, 0x97, 0x41, 0x91, 0x6A, 0x77, 0xB6, 0x37, 0xA6, 0x72, 0xF3, 0x2D,
0xF9, 0x19, 0x5C, 0x19, 0xFE, 0xEC, 0x80, 0x1F, 0xDE, 0xBF, 0xEE, 0x5E, 0xC4, 0x62, 0x4A, 0xBD,
0x91, 0x80, 0xA0, 0xE7, 0xD0, 0xC8, 0xF7, 0x94, 0xEC, 0x4B, 0x2A, 0x0B, 0xA0, 0x25, 0xF3, 0xE4,
0xB0, 0x60, 0x49, 0x13, 0xD7, 0x15, 0xEC, 0x22, 0x00, 0xD4, 0x3E, 0xBE, 0xB9, 0x46, 0x54, 0x75,
0xE4, 0x11, 0x4D, 0xBE, 0xAF, 0xFC, 0xE2, 0x3A, 0xC7, 0x54, 0x3F, 0x39, 0xC9, 0x8E, 0x11, 0x80,
0xFA, 0x2D, 0xE1, 0x4A, 0x37, 0xE7, 0xA0, 0x8C, 0x2F, 0x03, 0x83, 0x7D, 0xCF, 0x05, 0x11, 0x56,
0xFE, 0x8D, 0x7A, 0x56, 0x8B, 0x7D, 0x2A, 0x0A, 0x63, 0x5A, 0x9E, 0xCB, 0x08, 0xF5, 0x76, 0x88,
0x3E, 0x4A, 0xCC, 0xC6, 0x08, 0xA8, 0xC5, 0xE4, 0xD2, 0x12, 0x65, 0xA4, 0xF0, 0xE6, 0xA8, 0xE7,
0x47, 0xBC, 0xCE, 0xB8, 0x4A, 0x91, 0xF6, 0x8C, 0x29, 0x25, 0xEE, 0xC8, 0x44, 0xA1, 0x4A, 0x84,
0xE6, 0x7D, 0xB3, 0x8F, 0x30, 0xA1, 0x82, 0xB3, 0x53, 0xCA, 0x8E, 0x3A, 0x03, 0x30, 0x41, 0xB0,
0x28, 0x66, 0xF2, 0xEB, 0xD6, 0x81, 0x6E, 0x49, 0x7F, 0x7C, 0xD8, 0x39, 0x67, 0xDB, 0xB4, 0xFD,
0x59, 0x8F, 0x3E, 0xAE, 0x99, 0x1A, 0x00, 0x80, 0x19, 0xF3, 0xA3, 0x47, 0xEF, 0x2C, 0x06, 0x3A,
0x07, 0x52, 0x8D, 0xF7, 0x15, 0x95, 0xBC, 0xC8, 0xCE, 0x28, 0x04, 0x25, 0x58, 0xEB, 0xDC, 0x7A
};


/////////////////////////////////////////////////////////////////////////
// 自定义动态函数,函数名称不可更改
// 我们都是利用这个函数,对缓冲区进行编程前的操作,这个函数传递了所有芯片缓冲区的操作地址
// 你可以任意修改缓冲区的内容,你只要进行反向的加密算法,放入数据就可了
// 返回值: 返回对芯片缓冲区操作的区块结果的结构指针PSTRUCOPERATOR
// 参数: 6个缓冲区指针 (应用程序传给DLL的)
// pCode: 对应编程器程序区的数据指针
// pEEdata: 对应编程器数据区的数据指针
// pUserRow: 对应编程器USER ROW的数据指针,
// 在87C51编程时为Envrypt Table加密阵列的指针
// pID: 对应编程器ID区的数据指针
// pSerial: 前一次编程时的序列号
// pSerial 对应值必须为ASCII码字符串(最大长度不可超过32个字符)
// 这个值可以作为一个初始值使用,也可以不使用这个值,如果不使用,需要处理在DLL文件中保存并处理初始编号。
// 使用这个字符串,需要把新的初值放在这个缓冲内,写芯片时,应用程序会自动保存,
// 在编程下一个芯片时,新的值会传送到DLL,这样可省略在DLL文件的初值保存部分程序
// 你也可以把它看成这是个编程器主程序调用DLL的计数据使用
// pUniqueKey: 厂家设定的芯片唯一设别标志指针。对厂家每个芯片设有唯一序列号的单片机(并且无法更改的芯片),可用于自定义
// 加密算法,在程序中对该标志进行加密判断,使芯片中的原代码即使被克隆,也无法在另一个芯片上直接使用。
// 注:对于ATMEL AVR芯片虽没有唯一标识,但本编程器可自动读入RC校正字节,该字节不可更改,几十个芯片才有一个可能相同
// 用户可更改对应存储位置加密算法的数据,使程序只能在这个芯片上正常工作,就可实现对芯片进行加密
// (上电后,单片机软件读入OSCCAL寄存器,就是对应校正字节值)
// 有此方法,可方便实现量产,也可用于自动调入非默认的校正字节到程序中,
// 否则你需要对每个芯片的程序进行分别编译。
//////////////////////////////////////////////////////////////////////
GETSERIAL_API pSTRUCOPERATOR GetSerial(unsigned char * pCode,unsigned char * pEEdata,unsigned char * pUserRow,unsigned char *
pID,char * pSerial,unsigned char * pUniqueKey)
{
//你可在这里读取或改变缓冲区全部数据
//操作注意事项:
//1、对缓冲区的操作,包刮读取或更改内容不可大于当前芯片的容量大小
// 否则可能产生不可预料的严重重错误。
//2、可以6个任意的缓冲区内放入任何数据,例如放入加密算法后的数据
//3、对没有EEDATA 或USER ROW 或ID存储区的芯片,不可操作对应区块的缓存
//4、在允许自动编号的情况下,编程器每测试或编程前,调用一次这个函数。
int i;
unsigned char TempChar;

//随机重新生成一个table2表
srand( (unsigned)time( NULL ) ); //seed
for(i=0x0;i<256;i++)table2=(unsigned char)(rand()&0xFF);

//反向计算table1表,放入0x800处,0x800为单片机中table1表的位置
//这个过程必须与单片机的算法反向,单片机工作时,才能得到正确的值
for(i=0;i<256;i++)
{
//首先RC校正字节与table1明码表异或
TempChar=table1^pUniqueKey[0];
//结果与table[2]表异或,第一个开始位置在OSCCAL处
TempChar^=table2[(i+pUniqueKey[0])&0xFF];
//放入单片机中table1表的位置
pCode[i+0x800]=(unsigned char)TempChar;
}
//放入talbe2表到单片机0x900位置
for(i=0;i<256;i++)
{
pCode[i+0x900]=table2;
}

//////////////////////////////////////////////////////////////////////
//设置操作区块,提示对那些存储区块进行了编号操作
//如果测试时不需要界面显示,OprateResult.nCount=0; 后面的可不设定

OprateResult.nCount=2; //修改了2处

OprateResult.MemoryType[0] = CODE_MEMORY; //第1处的缓冲区类型
OprateResult.StartAddr[0] = 0x800; //第1处的开始地址
OprateResult.Len[0] = 256; //第1处的长度

OprateResult.MemoryType[1] = CODE_MEMORY; //第2处的缓冲区类型
OprateResult.StartAddr[1] = 0x900; //第2处的开始地址
OprateResult.Len[1] = 256; //第2处的长度

return &OprateResult; //返回操作结果
}

结束语:上面的源程序在TLL866编程器安装包内,方法不知是否合理,供大家参考

返回列表