|
本帖最后由 LscmunaixMG 于 2017-6-12 18:48 编辑
DIY单灯制令牌 史上成本最低的LED二进制动态密码保护器 全球首发!!!
是目前世界上制作成本最低、体积最小、制作最简单的的硬件版动态密码保护器。
产品工作原理:按键按下后LED随机产生常亮,闪烁状态,通过指定算法计算得到状态,服务端通过一致的算法进行搜索并且找到搜索位置即为按键次数。
第一代产品,由于算法存在很大的碰撞概率,只适用于便携计数器,不适用于高级密码学保护产品。
理论计数范围可无限大,实际上由于服务器搜索范围有限(可直接修改),每次验证失败需要占用大量的CPU资源和时间才能返回结果,且客户端输入信息需要较长时间即可完成。
软件准备:
USB转TTL 驱动程序 不同型号芯片有不同的驱动程序,请到官方网站或通过芯片型号搜索进行下载安装对应的驱动程序,硬件端即可正常运行。
Keil 汉化破解版 (将C源码编译HEX文件用) 以上软件各种组件以及注册机可能有杀毒软件误报病毒木马请选择信任即可或退出所有杀毒软件即可。
STC-ISP (STC单片机编程烧写软件) 官方网站有下载最新版本,无需安装,直接双击运行即可。
PHPnow (PHP&MYSQL服务器软件) 或者租用PHP服务器,各大注册商均有提供免费租用服务。
以上软件在官方网站以及各大互联网网站均有提供下载最新版本,具体下载地址请使用搜索引擎查找。
服务端采用纯文本文档保存验证数据,无需MySQL数据库即可实现,需要拥有创建、读写、修改文件的权限即可正常运行。
若运行时出现错误则文件读写受限或禁用了某些函数的功能,或服务器的php.ini配置存在问题。
硬件准备:
单片机 STC15L104E 1片 或者其他能在3v电压启动的C51指令集的8脚以内的单片机。
更换单片机需要注意P3.4口必须支持下降沿中断,否则不能唤醒中断而不能点亮,只能更换其他I/O口。
LED 任意颜色 1个 消耗电流越小越好,避免电池电量提前耗尽。
电阻(可选) 部分型号led需要串联电阻才能使功耗降到更低,具体阻值与led电压和电流有关。
建议加上 瓷片电容 104 和 10uf 电解电容,与电源并联,用于去除电源噪声,提高抗干扰能力。
轻触开关 1个 建议使用橡胶系列无声轻触开关,按键抖动时间更短,按键次数寿命更长。
CR2032 电池 1节 或者其他能使单片机启动电压的供电电源。
导线 若干 用来连接电路 或印刷PCB 线路 无需导线。
硬件电路连接方法:
LED与VCC连接在P3.5口
按键与GND连接在P3.4口
VCC与GND连接电池
下载选项:
IRC频率:8Mhz (使用其他芯片型号或更改其他频率需要修改延迟时间)
若芯片支持加密程序代码建议使用该功能,避免被客户解密破解泄漏算法。
下次冷启动时,P3.0/P3.3为0/0才可下载程序: 在出厂发布前建议勾上,并且掩盖芯片型号。
低压检测电压:1.89V 建议使用在该电压以内能点亮的led,可使本产品在更低电压下正常运行。
若需要开启EEPROM保存密码功能请将相关被注释包括的代码去除注释标识符即可开启该功能,且片内必须包含EEPROM。
若使用EEPROM来保存密码数据 不能勾选 低压时禁止EEPROM操作,否则可能出现验证失败或其他错误。
采用配置PCON寄存器的方法实现超低功耗待机,使用I/O口下降沿中断功能唤醒CPU工作,按键结束后CPU停止工作,持续使用电池保存所有内存数据。
若不使用EEPROM,将芯片和电池嵌入式焊死,而且不用加开关,至少1年无需更换。
验证按键次数过多或者失败会等待较长时间才能返回结果,搜索范围越大等待时间越长,具体处理速度及返回时间与服务器配置有关。
单片机程序准备:
按键按下,通过二进制伪随机数算法计算 ,随机产生LED常亮,闪烁状态,并且将密码数据保存到片内EEPROM中。
按键连续长按3秒以上,LED熄灭,避免LED长时间点亮导致电池电量提前耗尽。
软件准备:
产生一个与单片机一致的二进制伪随机数算法,并且客户需要一次输入多位。
验证开始后,服务器开始循环进行移位计算、搜索客户端输入的二进制密码。
搜索成功后,验证成功,并显示按键次数,最后将计算的密码位置数据保存到服务器内部数据库,下次验证从该位置继续搜索计算。
搜索失败,验证失败,不保存任何数据。
验证失败可能的原因:
1.按键未去抖,一次按键触发了多次计算。
2.电池接触不良或看门狗出现错误,导致复位。
3.算法不一致或变量类型存在错误。
4.芯片受到干扰或看门狗溢出,导致复位或其他错误。
5.按键次数超出密码搜索范围,可尝试加大密码搜索范围。
6.验证以后当前按键次数小于总按键次数,避免重复使用旧密码。
7.片内EEPROM出现错误,部分数据无法写入导致计算出错。
8.按键或 LED接触不良或 I/O口存在老化损坏现象。
按键次数错误的原因:
1.伪随机数算法存在BUG,出现重复的数据。
修改算法,并且通过该算法生成一个二进制文件,
使用各种压缩软件压缩该文件,压缩率≥100% 为合格的算法。
2.客户输入的位过少,或者出现了一个极小概率的碰撞。
若软硬件存在BUG或者建议意见等问题请在楼下跟帖回复。
下面是源码:
C51硬件源码(用keil编译后可直接烧写到单片机内):
-
- #include"reg51.h"
- #include"intrins.h"
- sbit led=P3^4; //LED对应IO口
- sbit key=P3^5; //按键对应IO口 (该口必须支持下降沿中断)
- unsigned char a=0;unsigned char b=0;unsigned char c=0;
- unsigned char d=0;unsigned char e=0;unsigned char f=0;
- unsigned char g=0;unsigned char h=0;unsigned char i=0;
- sfr INT_CLKO = 0x8F;
- /*
- sfr IAP_DATA = 0xC2;sfr IAP_ADDRH = 0xC3;sfr IAP_ADDRL = 0xC4;
- sfr IAP_CMD = 0xC5;sfr IAP_TRIG = 0xC6;sfr IAP_CONTR = 0xC7;
- void init(){ //EEPROM初始化
- IAP_CONTR=0x83 ;
- IAP_CMD=0x00 ;
- IAP_ADDRH=0x00 ;
- IAP_ADDRL=0x00 ;
- }
- unsigned char read_dat(unsigned char addr_h,unsigned char addr_l){ //从EEPROM读取数据
- unsigned char E_dat=0x00 ;
- IAP_CMD=0x01 ;
- IAP_ADDRH=addr_h ;
- IAP_ADDRL=addr_l ;
- IAP_TRIG=0x5A ;
- IAP_TRIG=0xA5 ;
- E_dat=IAP_DATA ;
- return E_dat ;
- }
- void erase_dat(unsigned char addr_h,unsigned char addr_l){//擦除EEPROM数据
- IAP_CMD=0x03 ;
- IAP_ADDRH=addr_h ;
- IAP_ADDRL=addr_l ;
- IAP_TRIG=0x5A ;
- IAP_TRIG=0xA5 ;
- }
- void write_dat(unsigned char addr_h,unsigned char addr_l,unsigned char w_dat){//往EEPROM写入数据
- IAP_CMD=0x02 ;
- IAP_ADDRH=addr_h ;
- IAP_ADDRL=addr_l ;
- IAP_DATA=w_dat ;
- IAP_TRIG=0x5A ;
- IAP_TRIG=0xA5 ;
- }
- */
- void delay25ms(){ //8MHz晶振 25毫秒延迟程序
- unsigned char i, j, k;
- _nop_();
- _nop_();
- i = 1;
- j = 195;
- k = 136;
- do
- {
- do
- {
- while (--k);
- } while (--j);
- } while (--i);
- }
- /*
- void read_pass(){//从EEPROM读取密码
- a=read_dat(0x00,0x00);
- b=read_dat(0x00,0x01);
- c=read_dat(0x00,0x02);
- d=read_dat(0x00,0x03);
- e=read_dat(0x00,0x04);
- f=read_dat(0x00,0x05);
- g=read_dat(0x00,0x06);
- h=read_dat(0x00,0x07);
- i=read_dat(0x00,0x08);
- }
- void write_pass(){ //写密码到EEPROM
- erase_dat(0x00,0x00);
- erase_dat(0x00,0x01);
- erase_dat(0x00,0x02);
- erase_dat(0x00,0x03);
- erase_dat(0x00,0x04);
- erase_dat(0x00,0x05);
- erase_dat(0x00,0x06);
- erase_dat(0x00,0x07);
- erase_dat(0x00,0x08);
- write_dat(0x00,0x00,a);
- write_dat(0x00,0x01,b);
- write_dat(0x00,0x02,c);
- write_dat(0x00,0x03,d);
- write_dat(0x00,0x04,e);
- write_dat(0x00,0x05,f);
- write_dat(0x00,0x06,g);
- write_dat(0x00,0x07,h);
- write_dat(0x00,0x08,i);
- }
- */
- bit get_pass(){ //计算位密码
- b=(c+3)*3;c=(d-3)*3;d=(e+3)*3;e=(f-3)*3;
- f=(g+3)*3;g=(h-3)*3;h=(i+3)*3;i=(b-3)*3;
- if(b > 127) c = c^127;
- if(c < 127) d = d^127;
- if(d > 127) e = e^127;
- if(e < 127) f = f^127;
- if(f > 127) g = g^127;
- if(g < 127) h = h^127;
- if(h > 127) i = i^127;
- if(i < 127) b = b^127;
- a=b^c^d^e^f^g^h^i;
-
-
- //write_pass();
- led=1;
- if((a&1)==0){
- return 0;
- } else {
- return 1;
- }
- }
- void main(){
- unsigned char n=0;//循环计数
- //init(); //初始化EEPROM
- INT_CLKO |= 0x20; //设置外部中断地址
- EA = 1; //开CPU中断
- //read_pass();
- for(n=0;n<=20;n++){ //循环20下
- delay25ms(); //延迟25毫秒
- led=~led; //led亮灭翻转
- }
- led=1; //led熄灭
- n=0;//清除计数
- while (1){ //死循环
- //read_pass(); //从EEPROM读取数据
- led=1; //led熄灭
- PCON = 0x02; //进入掉电模式,可通过外部中断唤醒,功耗约2ua,CPU不工作,只负责保存RAM数据。
- //CR2032容量约200ma,经过计算可维持芯片保存数据约100000小时(大约11年半)。
- //白发白高亮LED电流约20ma,持续点亮只能点亮10小时左右。
- _nop_(); //掉电模式唤醒后首先执行此语句
- _nop_();
- }
- }
- void exint3() interrupt 11{ //P3.5下降沿中断
- bit p=0; //密码
- unsigned char n=0; //延迟计数熄灭
- INT_CLKO &= 0xDF; //关中断
- delay25ms(); //延迟25毫秒
- if(key == 0){ //按键按下
- p=get_pass(); //计算密码
- while(key == 0 && n < 60){ //按键按下且计数在60以内
- //变量n主要实现防止按键长时间按住不放导致电池电量提前耗尽。
- if(p == 0){ //计算出密码0
- led=~led;//led闪烁
- } else { //计算出密码1
- led=0; //led常亮
- }
- delay25ms(); //延迟25毫秒
- delay25ms(); //延迟25毫秒
- n++; //计数+1
- }
- }
- led=1; //led熄灭
- INT_CLKO |= 0x20; //开中断
-
- }
复制代码
HTML 验证界面源码 (直接上传PHP服务器的任意目录即可运行,文件名:ddzlp.htm):
-
- 单灯制令牌 - 验证系统<br/>
- 按下按键后,LED直亮请输入1,闪烁请输入0,需要输入32位字符,不包含其他字符。<br/>
- 验证成功会显示按键次数,验证失败或按键次数过多需要等待较长时间才可以计算出验证结果。<br/>
- <form action="ddzlp.php" method="get">
- <input type="text" name="pass" maxlength="32" autocomplete="off" onkeypress = "return event.keyCode==48||event.keyCode==49" onpaste = "return !clipboardData.getData('text').match(/\D/)" ondragenter = "return false" style = "ime-mode:Disabled" />
- <input type="submit"/>
- </form>
复制代码
PHP 源码(与ddzlp.htm上传到同一个目录即可,文件名:ddzlp.php,上传成功后直接访问该文件即可,程序会自动调用文件ddzlp.htm,并且会自动创建一个data.txt文本文档,用来保存验证数据):
- <title>单灯制令牌 - 验证系统</title>
- <meta http-equiv="Content-Type" content="text/html; charset=gbk" />
- <?php
- set_time_limit(0);
- $按键次数=0;
- if(!file_exists("data.txt")){
- file_put_contents("data.txt","0,0,0,0,0,0,0,0,0,0");
- }
- $数据 = explode(",",file_get_contents("data.txt"));
- $逻辑数组="00000000000000000000000000000000";
- $A=$数据[0];
- $B=$数据[1];$C=$数据[2];$D=$数据[3];$E=$数据[4];
- $F=$数据[5];$G=$数据[6];$H=$数据[7];$I=$数据[8];
- $总按键次数 = $数据[9];
- function get_pass(){ //计算密码
- global $A,$B,$C,$D,$E,$F,$G,$H,$I;
- $B = ($C +3) *3;$B &= 0xFF;
- $C = ($D -3) *3;$C &= 0xFF;
- $D = ($E +3) *3;$D &= 0xFF;
- $E = ($F -3) *3;$E &= 0xFF;
- $F = ($G +3) *3;$F &= 0xFF;
- $G = ($H -3) *3;$G &= 0xFF;
- $H = ($I +3) *3;$H &= 0xFF;
- $I = ($B -3) *3;$I &= 0xFF;
- if($B > 127) $C = $C^127;
- if($C < 127) $D = $D^127;
- if($D > 127) $E = $E^127;
- if($E < 127) $F = $F^127;
- if($F > 127) $G = $G^127;
- if($G < 127) $H = $H^127;
- if($H > 127) $I = $I^127;
- if($I < 127) $B = $B^127;
- $A=$B^$C^$D^$E^$F^$G^$H^$I;
- $A=$A&1;
-
- return $A;
- }
- function a0n($text){ //搜索字符串中出现几个0
- $n = 0;
- $j = 0;
- for($n=0;$n<32;$n++){
- if($text[$n] == "0"){
- $j++;
- }
- }
- return $j;
- }
- function a1n($text){ //搜索字符串中出现几个1
- $n = 0;
- $j = 0;
- for($n=0;$n<32;$n++){
- if($text[$n] == "1"){
- $j++;
- }
- }
- return $j;
- }
- $pass = strrev(@$_GET["pass"]);
- if(strlen($pass) == 0){
- echo file_get_contents("ddzlp.htm");
- //exit("参数调用方法:文件名.php?pass=(密码,32位二进制数字)。");
- exit("");
- }
- if(strrev($pass) == "reset"){
- file_put_contents("data.txt","0,0,0,0,0,0,0,0,0,0");
- exit("数据复位成功!");
- }
- if(strlen($pass) != 32){
- exit("密码不等于32位二进制数字。");
- }
- if(a0n($pass) + a1n($pass) != 32){
- exit("密码包含非法字符。");
- }
- $内部逻辑数组="00000000000000000000000000000000";
- $循环次数 = 0;
- $取值计数 = 0;
- $按键次数=$总按键次数;
- for($循环次数=0;$循环次数<=65536;$循环次数++){
- for($取值计数=0;$取值计数<31;$取值计数++){
- $内部逻辑数组[$取值计数+1]=$逻辑数组[$取值计数];
- }
- $内部逻辑数组 [0] = get_pass();
- $逻辑数组 = $内部逻辑数组;
- if($循环次数 >= 31){
- //echo strrev($逻辑数组)."<br>";
- if($pass == $逻辑数组){ //验证成功
- $总按键次数 = $按键次数 + 32;
- file_put_contents("data.txt",$A.",".$B.",".$C.",".$D.",".$E.",".$F.",".$G.",".$H.",".$I.",".$总按键次数);
- exit("验证成功! 按键共按了".$总按键次数."次。");
- }
- $按键次数=$按键次数+1;
- }
- //echo get_pass();
- }
- exit("验证失败!");
- ?>
-
复制代码 |
|