MifareOneTool/MifareOneTool/ClassMifareS50.cs
2019-01-30 17:42:52 +08:00

444 lines
15 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using System.Text.RegularExpressions;
namespace MifareOneTool
{
class Utils
{
public static string Hex2Str(byte[] bytes)
{
StringBuilder ret = new StringBuilder();
foreach (byte b in bytes)
{
ret.AppendFormat("{0:x2}", b);
}
return ret.ToString();
}
public static string Hex2StrWithSpan(byte[] bytes)
{
StringBuilder ret = new StringBuilder();
foreach (byte b in bytes)
{
ret.AppendFormat("{0:x2}", b);
ret.Append(" ");
}
return ret.ToString();
}
public static byte[] Hex2Block(string hex, int bytelen)
{
hex = hex.Replace(" ", "");
byte[] returnBytes = new byte[bytelen];
for (int i = 0; i < bytelen; i++)
returnBytes[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
return returnBytes;
}
public static byte[] ReadAC(byte[] ac)
{
byte[] acbits = new byte[4];
acbits[0] = (byte)(((ac[2] & 0x10) >> 4)
+ ((ac[2] & 0x01) << 1)
+ ((ac[1] & 0x10) >> 2));
acbits[1] = (byte)(((ac[2] & 0x20) >> 5)
+ ((ac[2] & 0x02))
+ ((ac[1] & 0x20) >> 3));
acbits[2] = (byte)(((ac[2] & 0x40) >> 6)
+ ((ac[2] & 0x04) >> 1)
+ ((ac[1] & 0x40) >> 4));
acbits[3] = (byte)(((ac[2] & 0x80) >> 7)
+ ((ac[2] & 0x08) >> 2)
+ ((ac[1] & 0x80) >> 5));
return acbits;
}
public static byte[] ReadRAC(byte[] ac)
{
byte[] acbits = new byte[4];
for (int i = 0; i < ac.Length; i++)
{
ac[i] = (byte)~ac[i];
}
acbits[0] = (byte)(((ac[0] & 0x01) << 2)
+ ((ac[0] & 0x10) >> 3)
+ ((ac[1] & 0x01)));
acbits[1] = (byte)(((ac[0] & 0x02) << 1)
+ ((ac[0] & 0x20) >> 4)
+ ((ac[1] & 0x02) >> 1));
acbits[2] = (byte)(((ac[0] & 0x04))
+ ((ac[0] & 0x40) >> 5)
+ ((ac[1] & 0x04) >> 2));
acbits[3] = (byte)(((ac[0] & 0x08) >> 1)
+ ((ac[0] & 0x80) >> 6)
+ ((ac[1] & 0x08) >> 3));
return acbits;
}
public static byte[] GenAC(byte[] ac)
{
byte[] acbits = new byte[4];
acbits[3] = 0x00;
acbits[1] = (byte)(((ac[0] << 2) & 0x10)
| ((ac[1] << 3) & 0x20)
| ((ac[2] << 4) & 0x40)
| ((ac[3] << 5) & 0x80));
acbits[2] = (byte)(((ac[0] >> 1) & 0x01)
| ((ac[1]) & 0x02)
| ((ac[2] << 1) & 0x04)
| ((ac[3] << 2) & 0x08)
| ((ac[0] << 4) & 0x10)
| ((ac[1] << 5) & 0x20)
| ((ac[2] << 6) & 0x40)
| ((ac[3] << 7) & 0x80));
for (int i = 0; i < ac.Length; i++)
{
ac[i] = (byte)~ac[i];
}
acbits[1] = (byte)(acbits[1] |
((ac[0]) & 0x01)
| ((ac[1] << 1) & 0x02)
| ((ac[2] << 2) & 0x04)
| ((ac[3] << 3) & 0x08));
acbits[0] = (byte)(((ac[0] >> 2) & 0x01)
| ((ac[1] >> 1) & 0x02)
| ((ac[2]) & 0x04)
| ((ac[3] << 1) & 0x08)
| ((ac[0] << 3) & 0x10)
| ((ac[1] << 4) & 0x20)
| ((ac[2] << 5) & 0x40)
| ((ac[3] << 6) & 0x80));
return acbits;
}
//public static bool DtKeyAB(byte[] ac)
//{
// byte[] acbits = new byte[4];
// acbits[0] = (byte)(((ac[2] & 0x10) >> 4)
// + ((ac[2] & 0x01) << 1)
// + ((ac[1] & 0x10) >> 2));
// acbits[1] = (byte)(((ac[2] & 0x20) >> 5)
// + ((ac[2] & 0x02))
// + ((ac[1] & 0x20) >> 3));
// acbits[2] = (byte)(((ac[2] & 0x40) >> 6)
// + ((ac[2] & 0x04) >> 1)
// + ((ac[1] & 0x40) >> 4));
// acbits[3] = (byte)(((ac[2] & 0x80) >> 7)
// + ((ac[2] & 0x08) >> 2)
// + ((ac[1] & 0x80) >> 5));
// return acbits;
//}
}
enum AccessBitsT
{
KeyAW_KeyAR_KeyARW,
keyAW_KeyARW_KeyARW,
Never_KeyAR_KeyAR,
KeyBW_KeyABRKeyBW_KeyBW,
KeyBW_KeyABR_KeyBW,
Never_KeyABRKeyBW_Never,
Never_KeyABR_Never,
Never_KeyABR_Never2
}
enum AccessBitsD
{
AB_AB_AB_AB,
AB_N_N_AB,
AB_N_N_N,
B_B_N_N,
AB_B_N_N,
B_N_N_N,
AB_B_B_AB,
N_N_N_N
}
class Sector
{
private byte[][] _sector = new byte[4][] { new byte[16], new byte[16], new byte[16], new byte[16], };
public byte[][] Block
{
get { return _sector; }
set { _sector = value; }
}
private bool _isSector0 = false;
public bool IsSector0
{
get { return _isSector0; }
set { _isSector0 = value; }
}
public void Wipe()
{
byte[] zeroBlock = this._sector[0];
this._sector = new byte[4][]{
new byte[16]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
new byte[16]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
new byte[16]{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},
new byte[16]{0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0x07,0x80,0x69,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF},
};
if (this._isSector0)
{
this._sector[0] = zeroBlock;
}
}
public Sector(bool sector0 = false)
{
this._isSector0 = sector0;
this.Wipe();
if (sector0)
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] uid = new byte[4];
rng.GetNonZeroBytes(uid);
byte bcc = (byte)(uid[0] ^ uid[1] ^ uid[2] ^ uid[3]);
this._sector[0] = new byte[16] { uid[0], uid[1], uid[2], uid[3], bcc, 0x08, 0x04, 0x00,
0x62, 0x63, 0x64, 0x65, 0x66,0x67,0x68,0x69 };
}
}
public Sector(byte[] uid)
{
if (uid.Length != 4) { throw new Exception("不恰当的4字节UID长度"); }
this._isSector0 = true;
this.Wipe();
byte bcc = (byte)(uid[0] ^ uid[1] ^ uid[2] ^ uid[3]);
this._sector[0] = new byte[16] { uid[0], uid[1], uid[2], uid[3], bcc, 0x08, 0x04, 0x00,
0x62, 0x63, 0x64, 0x65, 0x66,0x67,0x68,0x69 };
}
public int Verify()
{
/* 检验该块内容是否合法
* 0块检查BCC
* 非0块检查访问控制
* ********
* 0000正常
* 0001BCC错
* 0010访问控制无效
* 0100访问控制损坏
*
*/
int retCode = 0;
if (this._isSector0)
{
byte bc0 = (byte)(_sector[0][0] ^ _sector[0][1] ^ _sector[0][2] ^ _sector[0][3] ^ _sector[0][4]);
if (bc0 != 0x00) { retCode = retCode | 0x01; }
}
byte[] ac = new byte[4] { _sector[3][6], _sector[3][7], _sector[3][8], _sector[3][9] };
byte[] acP = Utils.ReadAC(ac);
byte[] acN = Utils.ReadRAC(ac);
if (!Enumerable.SequenceEqual(acP, acN))
{
retCode = retCode | 0x04;
}
foreach (byte acc in acP)
{
if (acc > 0x08)
{
retCode = retCode | 0x02;
break;
}
}
return retCode;
}
public string Info(int sec)
{
string info = "扇区" + sec.ToString();
if (Enumerable.SequenceEqual(
new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
this._sector[0]) &&
Enumerable.SequenceEqual(
new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
this._sector[1]) &&
Enumerable.SequenceEqual(
new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
this._sector[2]))
{
info += " 空扇区";
}
else
{
info += " 有数据";
}
if (this.Verify() != 0x00)
{
info += " 有错误";
}
return info;
}
public byte[] KeyA
{
get { return this._sector[3].Skip(0).Take(6).ToArray(); }
set { for (int i = 0; i < 6; i++) { this._sector[3][i] = value[i]; } }
}
public byte[] KeyB
{
get { return this._sector[3].Skip(10).Take(6).ToArray(); }
set { for (int i = 10; i < 16; i++) { this._sector[3][i] = value[i]; } }
}
public byte[] ACBits
{
get { return this._sector[3].Skip(6).Take(4).ToArray(); }
set { for (int i = 6; i < 10; i++) { this._sector[3][i] = value[i]; } }
}
}
class S50
{
private List<Sector> _sectors = new List<Sector>(16);
internal byte[] SectorsRaw
{
get
{
byte[] buffer = new byte[1024];
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 16; k++)
{
buffer[i * 64 + j * 16 + k] = this._sectors[i].Block[j][k];
}
}
}
return buffer;
}
set
{
for (int i = 0; i < 16; i++)
{
for (int j = 0; j < 4; j++)
{
for (int k = 0; k < 16; k++)
{
this._sectors[i].Block[j][k] = value[i * 64 + j * 16 + k];
}
}
}
}
}
public List<Sector> Sectors
{
get { return _sectors; }
set { _sectors = value; }
}
public S50()
{
_sectors.Capacity = 16;
for (int i = 0; i < 16; i++)
{
if (i == 0) { _sectors.Add(new Sector(true)); }
else { _sectors.Add(new Sector()); }
}
}
public S50(byte[] uid)
{
_sectors.Capacity = 16;
if (uid.Length != 4) { throw new Exception("不恰当的4字节UID长度"); }
for (int i = 0; i < 16; i++)
{
if (i == 0) { _sectors.Add(new Sector(uid)); }
else { _sectors.Add(new Sector()); }
}
}
public void Wipe()
{
for (int i = 0; i < 16; i++)
{
_sectors[i].Wipe();
}
}
public int[] Verify()
{
int[] ret = new int[17];
int t = 0;
for (int i = 0; i < 16; i++)
{
ret[i] = _sectors[i].Verify();
t += ret[i];
}
ret[16] = t;
return ret;
}
public int Verify(int sector)
{
return _sectors[sector].Verify();
}
public void LoadFromMfd(string file)
{
if (!File.Exists(file)) { throw new IOException("加载的文件不存在。"); }
if (new FileInfo(file).Length != 1024) { throw new IOException("加载的S50卡文件大小异常。"); }
byte[] loadByte = File.ReadAllBytes(file);
this.Wipe();
this.SectorsRaw = (byte[])loadByte;
}
public void LoadFromMctTxt(string file)
{
if (!File.Exists(file)) { throw new IOException("加载的文件不存在。"); }
long fileLength=new FileInfo(file).Length;
if (fileLength < 2200 || fileLength > 2400) { throw new IOException("加载的S50卡文件大小异常。"); }
List<string> lines = new List<string>(File.ReadAllLines(file));
List<string> invaild = new List<string>();
foreach (string line in lines)
{
if (!Regex.IsMatch(line, "[0-9A-Fa-f]{32}") || line.Length != 32)
{
invaild.Add(line);
}
}
foreach (string inv in invaild)
{
lines.Remove(inv);
}
if (lines.Count != 64)
{
throw new Exception("文件内不是含有64个块数据可能不完整或不兼容。");
}
this.Wipe();
for (int i = 0; i < lines.Count; i++)
{
this._sectors[i / 4].Block[i % 4] = Utils.Hex2Block(lines[i], 16);
}
}
public void ExportToMfd(string file)
{
byte[] fileBuffer = this.SectorsRaw;
File.WriteAllBytes(file, fileBuffer);
}
public void ExportToMctTxt(string file)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 16; i++)
{
sb.AppendLine("+Sector: " + i.ToString());
for (int j = 0; j < 4; j++)
{
sb.AppendLine(Utils.Hex2Str(this._sectors[i].Block[j]));
}
}
File.WriteAllText(file, sb.ToString());
}
public List<byte[]> KeyList()
{
List<byte[]> keys = new List<byte[]>();
foreach (Sector s in this._sectors)
{
keys.Add(s.KeyA);
keys.Add(s.KeyB);
}
keys = keys.Distinct().ToList();
return keys;
}
public List<string> KeyListStr()
{
List<string> keys = new List<string>();
foreach (Sector s in this._sectors)
{
keys.Add(Utils.Hex2Str(s.KeyA));
keys.Add(Utils.Hex2Str(s.KeyB));
}
keys = keys.Distinct().ToList();
return keys;
}
}
}