您现在的位置是:首页 >技术杂谈 >Qt_C++读写M1IC卡源码支持windows国产linux操作系统网站首页技术杂谈

Qt_C++读写M1IC卡源码支持windows国产linux操作系统

津津有味道 2024-06-30 18:01:02
简介Qt_C++读写M1IC卡源码支持windows国产linux操作系统

  Android Linux RFID读写器NFC发卡器WEB可编程NDEF文本/智能海报/-淘宝网 (taobao.com)

 IC卡的特性

1、分为16个扇区,每个扇区为4块,每块16个字节,以块为存取单位;
2、每个扇区有独立的一组密码及访问控制;
3、每张卡有唯一序列号,为32位;
4、具有防冲突机制,支持多卡操作;
5、无电源,自带天线,内含加密控制逻辑和通讯逻辑电路;
6、数据保存期为10年,可改写10万次,读无限次;
7、工作温度:-20℃~50℃(湿度为90%);
8、工作频率:13.56MHZ;
9、通信速率:106 KBPS;
10、读写距离:10 cm以内。 

 IC卡的存储结构

1、M1卡分为16个扇区,每个扇区由4块(块0、块1、块2、块3)组成,(我们也将16个扇区的64个块按绝对地址编号为0~63,存贮结构如下图所示: 

2、第0扇区的块0(即绝对地址0块),它用于存放厂商代码,已经固化,不可更改。

3、每个扇区的块0、块1、块2为数据块,可用于存贮数据。

4、每个扇区的块3为控制块,包括了密码A、存取控制、密码B;

5、每个扇区的密码和存取控制都是独立的,可以根据实际需要设定各自的密码及存取控制。存取控制为4个字节,共32位,扇区中的每个块(包括数据块和控制块)的存取条件是由密码和存取控制共同决定的,在存取控制中每个块都有相应的三个控制位。 

#include "mainwindow.h"
#include "./ui_mainwindow.h"
#include <QDebug>
#include "QLibrary"
#include "QMessageBox"

#define BLOCK0_EN   0x01   //读写块0
#define BLOCK1_EN   0x02   //读写块1
#define BLOCK2_EN   0x04   //读写块2
#define NEEDSERIAL  0x08   //写指定序列号的卡
#define EXTERNKEY   0x10   //需要每次指定密码
#define NEEDHALT    0x20   //写卡后是否休眠卡


//让读写器发出声音
typedef  unsigned char (*pcdbeep)(unsigned long xms);
#ifdef Q_OS_WIN
    pcdbeep mypcdbeep = (pcdbeep)QLibrary("OUR_MIFARE.dll").resolve("pcdbeep");
#else
    pcdbeep mypcdbeep = (pcdbeep)QLibrary("./libOURIDR.so").resolve("pcdbeep");
#endif

//返回本读写器独一无二的设备编号
typedef  unsigned char (*pcdgetdevicenumber)(unsigned long devicenumber);
#ifdef Q_OS_WIN
    pcdgetdevicenumber mypcdgetdevicenumber = (pcdgetdevicenumber)QLibrary("OUR_MIFARE.dll").resolve("pcdgetdevicenumber");
#else
    pcdgetdevicenumber mypcdgetdevicenumber = (pcdgetdevicenumber)QLibrary("./libOURIDR.so").resolve("pcdgetdevicenumber");
#endif

//轻松读卡
typedef unsigned char (*piccreadex)(unsigned char ctrlword,unsigned char *serial,unsigned char area,unsigned char keyA1B0,unsigned char *picckey,unsigned char *piccdata0_2);
#ifdef Q_OS_WIN
    piccreadex mypiccreadex = (piccreadex)QLibrary("OUR_MIFARE.dll").resolve("piccreadex");
#else
    piccreadex mypiccreadex = (piccreadex)QLibrary("./libOURIDR.so").resolve("piccreadex");
#endif

//轻松写卡
typedef unsigned char (*piccwriteex)(unsigned char ctrlword,unsigned char *serial,unsigned char area, unsigned char keyA1B0,unsigned char *picckey,unsigned char *piccdata0_2);
#ifdef Q_OS_WIN
    piccwriteex mypiccwriteex = (piccwriteex)QLibrary("OUR_MIFARE.dll").resolve("piccwriteex");
#else
    piccwriteex mypiccwriteex = (piccwriteex)QLibrary("./libOURIDR.so").resolve("piccwriteex");
#endif

//改单区密码
typedef unsigned char (*piccchangesinglekeyex)(unsigned char ctrlword,unsigned char *serial,unsigned char area,unsigned char keyA1B0,unsigned char *piccoldkey,unsigned char *piccdata);
#ifdef Q_OS_WIN
    piccchangesinglekeyex mypiccchangesinglekeyex = (piccchangesinglekeyex)QLibrary("OUR_MIFARE.dll").resolve("piccchangesinglekeyex");
#else
    piccchangesinglekeyex mypiccchangesinglekeyex = (piccchangesinglekeyex)QLibrary("./libOURIDR.so").resolve("piccchangesinglekeyex");
#endif

//读出一块的数据,也就是16个字节
typedef unsigned char (*piccread)(unsigned char block,unsigned char *piccdata);
#ifdef Q_OS_WIN
    piccread mypiccread = (piccread)QLibrary("OUR_MIFARE.dll").resolve("piccread");
#else
    piccread mypiccread = (piccread)QLibrary("./libOURIDR.so").resolve("piccread");
#endif


//检测输入数据是否为16进制数
static bool checkinput(QString inputstr){
    QString inputyes="0123456789abcdefABCDEF";
    for(int i=0;i<inputstr.length();i++){
        if(inputyes.contains(inputstr.mid(i,1),Qt::CaseSensitive)){
        }else{return false;}
    }
    return true;
}

//检测动态库文件是否在运行目录内
static bool checkdllos(){
#ifdef Q_OS_WIN
    QLibrary mylib("OUR_MIFARE.dll");
    if (!mylib.load()){                //判断windows系统下,OUR_MIFARE.dll是否在运行目录内
        QMessageBox::information(NULL, "提示", "请将OUR_MIFARE.dll文件拷贝到生成exe文件相同目录下!");
        return false;
    }else{return true;}
#else
    QLibrary mylib("./libOURIDR.so");
    if (!mylib.load()){                //判断linux系统下,libOURIDR.so是否正运行目录内
        QMessageBox::information(NULL, "提示", "请将libOURIDR.so文件拷贝到生成的运行文件相同目录下!");
        return false;
    }else{return true;}
#endif
}


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    ui->comboBox_2->setCurrentIndex(1);    
}


MainWindow::~MainWindow()
{
    delete ui;
}

//显示返回的错误代码信息
static void disperrinf(unsigned char result){
    switch (result) {
    case 1:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",0~2块都没读出来,可能刷卡太块。但卡序列号已被读出来!");
        break;
    case 2:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",第0块已被读出,但1~2块读取失败。卡序列号已被读出来!");
        break;
    case 3:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",第0、1块已被读出,但2块读取失败。卡序列号已被读出来!");
        break;
    case 8:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",未寻到卡,请重新拿开卡后再放到感应区!");
        break;
    case 9:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",有多张卡在感应区,寻卡过程中防冲突失败,读序列吗错误!");
        break;
    case 10:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",该卡可能已被休眠,无法选中卡片!");
        break;
    case 11:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",密码装载失败!");
        break;
    case 12:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",卡片密码认证失败!");
        break;
    case 13:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",读指定块失败,原因是刷卡太快或本块所对应的区还没通过密码认证!");
        break;
    case 14:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",写指定块失败,原因是刷卡太快或本块所对应的区还没通过密码认证!");
        break;
    case 18:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",写UID失败,此卡可能不是UID卡!");
        break;
    case 22:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",动态库或驱动程序异常!");
        break;
    case 23:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",驱动程序错误或发卡器尚未安装!");
        break;
    case 24:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",操作超时,一般是动态库没有反应!");
        break;
    case 25:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",发送字数不够!");
        break;
    case 26:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",驱动程序错误或发卡器尚未安装!");
        break;
    case 27:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",操作超时,一般是动态库没有反应!");
        break;
    case 28:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",发送字数不够!");
        break;
    default:
        QMessageBox::critical(NULL, "提示", QString::asprintf("错误代码:%d", result)+",未知的错误信息!");
        break;
    }
}


void MainWindow::on_pushButton_4_clicked()//让设备发出声响
{
    if(!checkdllos()){return;}

    unsigned char status;
    status = mypcdbeep(30);
    if(status==0){
    }
    else
    {
        disperrinf(status);
    }
}


void MainWindow::on_pushButton_3_clicked()//轻松读卡
{
    if(!checkdllos()){return;}

    unsigned char ctrlword;         //控制字
    unsigned char serial[4];        //可写数组,存放4字节卡UID
    unsigned char area;             //读出的区号
    unsigned char keyA1B0;          //AB密码认证
    unsigned char picckey[6];       //存放卡密码的数组
    unsigned char piccdata0_2[48];  //下标0~15存放作为读出的块0的数据,下标16~31存放作为读出的块1的数据,下标32~47存放作为读出的块2的数据
    unsigned char status;           //返回
    unsigned char piccdata[16];     //卡数据缓冲

    //以下控制字含义:读块0、块1、块2,仅读指定序列号的卡,需要每次指定密码
    if (ui->radioButton->isChecked()){
        ctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY;  //外部密码
    }else{
        ctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN;  //内部密码
    }
    area = ui->comboBox->currentIndex();           //操作扇区号
    keyA1B0 = ui->comboBox_2->currentIndex();      //密码认证类型,;大于0用A密码 ,等于0用B密码

    QString Key_str = ui->lineEdit->text().trimmed();
    if(Key_str.length()==12 and checkinput(Key_str)){
        for(int i=0;i<6;i++){
            bool ok;
            picckey[i]=QString(Key_str.mid(i*2,2)).toInt(&ok,16);
        }
        status = mypiccreadex(ctrlword,serial,area,keyA1B0,picckey,piccdata0_2);    //读取选定扇区的0、1、2块
        if(status == 0){
            QString blockdata="";
            for(int i=0;i<16;i++){
                blockdata=blockdata+QString::asprintf("%02X ", piccdata0_2[i]);
            }
            ui->lineEdit_2->setText(blockdata);
            blockdata="";
            for(int i=16;i<32;i++){
                blockdata=blockdata+QString::asprintf("%02X ", piccdata0_2[i]);
            }
            ui->lineEdit_3->setText(blockdata);
            blockdata="";
            for(int i=32;i<48;i++){
                blockdata=blockdata+QString::asprintf("%02X ", piccdata0_2[i]);
            }
            ui->lineEdit_4->setText(blockdata);

            status = mypiccread(area * 4 + 3,piccdata);   //读取选定扇区的第3块 A密码、控制位、B密码,无法读取的信息返回 00
            if(status == 0){
                blockdata="";
                for(int i=0;i<16;i++){
                    blockdata=blockdata+QString::asprintf("%02X ", piccdata[i]);
                }
                ui->lineEdit_5->setText(blockdata);
                mypcdbeep(30);
            }else{
                ui->lineEdit_5->clear();
                disperrinf(status);
            }

        }else{
            ui->lineEdit_2->clear();
            ui->lineEdit_3->clear();
            ui->lineEdit_4->clear();
            ui->lineEdit_5->clear();
            disperrinf(status);
        }
    }else{
        QMessageBox::critical(NULL, "提示","请输入12位16进制卡片认证密码!");
    }
}


void MainWindow::on_pushButton_12_clicked()//轻松写卡
{
    if(!checkdllos()){return;}

    unsigned char ctrlword;        //控制字
    unsigned char serial[4];       //可写数组
    unsigned char area;            //操作扇区区号
    unsigned char keyA1B0;         //AB密码认证
    unsigned char picckey[6];      //存放卡密码的数组
    unsigned char piccdata0_2[48]; //下标0~15存放作为读出的块0的数据,下标16~31存放作为读出的块1的数据,下标32~47存放作为读出的块2的数据
    unsigned char status;          //返回
    char s;

    //以下控制字含义:读块0、块1、块2,仅读指定序列号的卡,需要每次指定密码
    if (ui->radioButton->isChecked()){
        ctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY;  //外部密码
    }else{
        ctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN;  //内部密码
    }
    area = ui->comboBox->currentIndex();           //操作扇区号
    keyA1B0 = ui->comboBox_2->currentIndex();      //密码认证类型,;大于0用A密码 ,等于0用B密码

    QString Key_str = ui->lineEdit->text().trimmed();
    if(Key_str.length()==12 and checkinput(Key_str)){
        for(int i=0;i<6;i++){
            bool ok;
            picckey[i]=QString(Key_str.mid(i*2,2)).toInt(&ok,16);
        }
    }else{
        QMessageBox::critical(NULL, "提示","请输入12位16进制卡片认证密码!");
        return;
    }

    //检测写卡数据
    QChar ch;
    QString writinf = ui->lineEdit_2->text().trimmed()+ui->lineEdit_3->text().trimmed()+ui->lineEdit_4->text().trimmed();
    QString writinf1 = "";
    int m = 0;
    int n = 0;

    ch = writinf.at(0);
    s = writinf.at(0).toLatin1();
    for(int i=0; i<writinf.length() ; i++)
    {
        ch = writinf.at(i);
        s = writinf.at(i).toLatin1();
        if((('0' <= s) && (s <= '9')) || (('A' <= s) && (s <= 'F')) || (('a' <= s) && (s <= 'f')))
        {
            writinf1 += ch;
            n++;
            if(n==2){
                bool ok;
                piccdata0_2[m++] = writinf1.toInt(&ok,16);
                writinf1 = "";
                n = 0;
                if(m>=48){
                    break;
                }
            }
        }
        else if(s == ' '){

        }
        else
        {
            if(m>=0 && m<16){
                QMessageBox::critical(NULL, "提示", "第0块写卡数据错误,请输入正确的16进制数据!");
                ui->lineEdit_2->setSelection(m%16*3,2);
                ui->lineEdit_2->setFocus();
            }else if(m>=16 && m<32){
                QMessageBox::critical(NULL, "提示", "第1块写卡数据错误,请输入正确的16进制数据!");
                ui->lineEdit_3->setSelection(m%16*3,2);
                ui->lineEdit_3->setFocus();
            }else if(m>=32 && m<48){
                QMessageBox::critical(NULL, "提示", "第2块写卡数据错误,请输入正确的16进制数据!");
                ui->lineEdit_4->setSelection(m%16*3,2);
                ui->lineEdit_4->setFocus();
            }
            return;
        }
    }

    status = mypiccwriteex(ctrlword,serial,area,keyA1B0,picckey,piccdata0_2);
    if(status == 0)
    {
        mypcdbeep(30);
        QMessageBox::information(NULL, "提示", "写卡成功!");
    }else{
        disperrinf(status);
    }
}

//改AB密码及控制字
void MainWindow::on_pushButton_14_clicked()
{
    if(!checkdllos()){return;}

    unsigned char ctrlword;     //控制字
    unsigned char serial[4];    //可写数组
    unsigned char area;         //操作扇区号
    unsigned char keyA1B0;      //AB密码认证方式
    unsigned char piccoldkey[6];//存放卡原始密码的数组
    unsigned char piccdata[17]; //存放卡新A密码(也就是准备改成的密码)、密码权限访问字、新B密码的数组、指定更改项目的标志
    unsigned char status;       //返回

    //以下控制字含义:读块0、块1、块2,仅读指定序列号的卡,需要每次指定密码
    if (ui->radioButton->isChecked()){
        ctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN + EXTERNKEY;  //外部密码
    }else{
        ctrlword = BLOCK0_EN + BLOCK1_EN + BLOCK2_EN;  //内部密码
    }
    area = ui->comboBox->currentIndex();               //操作扇区号
    keyA1B0 = ui->comboBox_2->currentIndex();          //密码认证类型,;大于0用A密码 ,等于0用B密码

    QString Key_str = ui->lineEdit->text().trimmed();
    if(Key_str.length()==12 and checkinput(Key_str)){
        for(int i=0;i<6;i++){
            bool ok;
            piccoldkey[i]=QString(Key_str.mid(i*2,2)).toInt(&ok,16);
        }
    }else{
        QMessageBox::critical(NULL, "提示","请输入12位16进制卡片认证密码!");
        return;
    }

    QChar ch;
    QString writinf = ui->lineEdit_5->text().trimmed();
    QString writinf4 = "";
    int m = 0;
    int n = 0;
    char s;
    for(int i=0; i<writinf.length() ; i++)
    {
        ch = writinf.at(i);
        s = writinf.at(i).toLatin1();
        if((('0' <= s) && (s <= '9')) || (('A' <= s) && (s <= 'F')) || (('a' <= s) && (s <= 'f')))
        {
            writinf4 += ch;
            n++;
            if(n==2){
                bool ok;
                piccdata[m++] = writinf4.toInt(&ok,16);
                writinf4 = "";
                n = 0;
                if(m>=16){
                    break;
                }
            }
        }
        else if(s == ' ') {

        }
        else
        {
            QMessageBox::critical(NULL, "提示", "新A、B密码及控制位输入错误,请输入正确的16进制数据!");
            ui->lineEdit_5->setSelection(i,1);
            ui->lineEdit_5->setFocus();
            return;
        }
    }

    if(m<16)
    {
        QMessageBox::critical(NULL, "提示", "新A、B密码及控制位输入错误,请输入正确的16进制数据!");
        ui->lineEdit_5->setFocus();
        return;
    }

    piccdata[16]=0x03;   //取值0表示只改A密码,2表示改A、B密码,3表示修改A、B及控制位

    int answ=QMessageBox::question (this, "警告", "你确定修改密码控制块的数据吗 ?此数据块如果改写错误,数据卡将会被限制!", QMessageBox::Ok, QMessageBox::Cancel);
    if (answ ==1024){
        status = mypiccchangesinglekeyex(ctrlword,serial,area,keyA1B0,piccoldkey,piccdata);
        if(status == 0){
            mypcdbeep(30);
            QMessageBox::information(NULL, "提示", "操作成功,写卡数据有效!");
        }else {
            disperrinf(status);
        }
    }
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。