Skip to content

Instantly share code, notes, and snippets.

@koenieee
Last active April 26, 2024 07:21
Show Gist options
  • Save koenieee/bea8eee12a463877c839d841eebe8993 to your computer and use it in GitHub Desktop.
Save koenieee/bea8eee12a463877c839d841eebe8993 to your computer and use it in GitHub Desktop.
Magic gen3 chinese changable UUID cards libnfc support - Piswords PCSC Mifare
====================== PCSC Mifare replacement ======================
Own implementation of the software used with the ACR122u reader software called PCSC Mifare from Piswords (a chinese seller).
Aliexpress sells these 7-byte changable UUID cards for Mifare Classic. Unfortanly it's not a normal magic card.
It's like a magic magic magic card.
They implemented a custom command themself. I didn't have a proxmark3, or ACR122u reader.
They also had software with uses the PC/SC API for their custom commands.
I only had a raspberry pi with the PN532 reader
=================================================
Unfortunately I would NOT recommend this card, it is read fine by my phone and the PN532 reader.
But the reader where it was meant for, couldn't read the card.
Don't buy it, if you already did. Try this gist and maybe you have better luck than me.
==================================================
I wrote my own implementation and port of the special Proxmark3 commands.
APDU commands:
cla ins p1 p2 len
90 F0 CC CC 10 <block0> - write block 0
90 FB CC CC 07 <uid> - change uid (independently of block0 data)
90 FD 11 11 00 - lock permanently
Proxmark uses these commands:
# change just UID:
hf 14a raw -s -c -t 2000 90FBCCCC07 11223344556677
# read block0:
hf 14a raw -s -c 3000
# write block0:
hf 14a raw -s -c -t 2000 90F0CCCC10 041219c3219316984200e32000000000
# lock (uid/block0?) forever:
hf 14a raw -s -c 90FD111100
=================== Specs ==========================
Specs for the Mifare card are:
It seems the length byte gets ignored anyway.
Note: it seems some cards only accept the "change UID" command.
It accepts direct read of block0 (and only block0) without prior auth.
Writing to block 0 has some side-effects:
It changes also the UID. Changing the UID does not change block 0.
ATQA and SAK bytes are automatically replaced by fixed values.
On 4-byte UID cards, BCC byte is automatically corrected.
Source: https://github.com/RfidResearchGroup/proxmark3/blob/master/doc/magic_cards_notes.md#mifare-classic-apdu-aka-gen3
=================== Source code of APDU command for RASPBERRY PI AND PN532 =====================
Change your UID on this line: memcpy(capdu, "\x90\xFB\xCC\xCC\x07\x06\xd3\xd1\x4a\x7b\x17\x90", 12);
You can decide to lock the UID with the command in the header...
After the \x07 byte
Compile like this: gcc magic_gen3.c -o magic_gen3 -lnfc
If you get linker errors, install lib-nfc dev: sudo apt-get install libnfc-dev (i think)
Run it like this: ./magic_gen3
Make sure ofc your libnfc device is working...
========================== C- code ===========================
#include <stdlib.h>
#include <string.h>
#include <nfc/nfc.h>
/*
Inspired by proxmark3. Can be use with PN532 reader and raspberry pi, so you don't need a proxmark3 device or PCSC Mifare software from piswords.
APDU commands for Magic Gen3 card.
cla ins p1 p2 len
90 F0 CC CC 10 <block0> - write block 0
90 FB CC CC 07 <uid> - change uid (independently of block0 data)
90 FD 11 11 00 - lock permanently
*/
void hexDump(char *desc, void *addr, int len)
{
int i;
unsigned char buff[17];
unsigned char *pc = (unsigned char*)addr;
// Output description if given.
if (desc != NULL)
printf ("%s:\n", desc);
// Process every byte in the data.
for (i = 0; i < len; i++) {
// Multiple of 16 means new line (with line offset).
if ((i % 16) == 0) {
// Just don't print ASCII for the zeroth line.
if (i != 0)
printf(" %s\n", buff);
// Output the offset.
printf(" %04x ", i);
}
// Now the hex code for the specific character.
printf(" %02x", pc[i]);
// And store a printable ASCII character for later.
if ((pc[i] < 0x20) || (pc[i] > 0x7e)) {
buff[i % 16] = '.';
} else {
buff[i % 16] = pc[i];
}
buff[(i % 16) + 1] = '\0';
}
// Pad out last line if not exactly 16 characters.
while ((i % 16) != 0) {
printf(" ");
i++;
}
// And print the final ASCII bit.
printf(" %s\n", buff);
}
int CardTransmit(nfc_device *pnd, uint8_t * capdu, size_t capdulen, uint8_t * rapdu, size_t * rapdulen)
{
int res;
size_t szPos;
uint32_t cycles = 0;
printf("=> ");
for (szPos = 0; szPos < capdulen; szPos++) {
printf("%02x ", capdu[szPos]);
}
printf("\n");
if ((res = nfc_initiator_transceive_bytes_timed(pnd, capdu, capdulen, rapdu, *rapdulen, &cycles)) < 0) { //can also be not timed, depens what works for u.
printf("error smaller than 0, res: %d, cycles %d\n", res, cycles);
return -1;
} else {
*rapdulen = (size_t) res;
printf("<= ");
for (szPos = 0; szPos < *rapdulen; szPos++) {
printf("%02x ", rapdu[szPos]);
}
printf("\n");
return 0;
}
}
int main(int argc, const char *argv[])
{
nfc_device *pnd;
nfc_target nt;
nfc_context *context;
nfc_init(&context);
if (context == NULL) {
printf("Unable to init libnfc (malloc)\n");
exit(EXIT_FAILURE);
}
const char *acLibnfcVersion = nfc_version();
(void)argc;
printf("%s uses libnfc %s\n", argv[0], acLibnfcVersion);
pnd = nfc_open(context, NULL);
if (pnd == NULL) {
printf("ERROR: %s", "Unable to open NFC device.");
exit(EXIT_FAILURE);
}
if (nfc_initiator_init(pnd) < 0) {
nfc_perror(pnd, "nfc_initiator_init");
exit(EXIT_FAILURE);
}
printf("NFC reader: %s opened\n", nfc_device_get_name(pnd));
const nfc_modulation nmMifare = {
.nmt = NMT_ISO14443A,
.nbr = NBR_106,
};
// nfc_set_property_bool(pnd, NP_AUTO_ISO14443_4, true);
printf("Polling for target...\n");
while (nfc_initiator_select_passive_target(pnd, nmMifare, NULL, 0, &nt) <= 0);
printf("Target detected!\n");
// Use raw send/receive methods
if (nfc_device_set_property_bool(pnd, NP_EASY_FRAMING, false) < 0) { //for timed function.
nfc_perror(pnd, "nfc_device_set_property_bool");
nfc_close(pnd);
nfc_exit(context);
exit(EXIT_FAILURE);
}
uint8_t capdu[264]={0};
size_t capdulen;
uint8_t rapdu[264]={0};
size_t rapdulen;
//change uid (command) + new uid
memcpy(capdu, "\x90\xFB\xCC\xCC\x07\x06\xd3\xd1\x4a\x7b\x17\x90", 12);
//hex value after: "\x90\xFB\xCC\xCC\x07 <UID> (in hex, use \x)
// first hex part is APDU command.
// to lock the UUID use this: '90 FD 11 11 00' APDU command
// Write sector 0 (See top)
capdulen=12; //make sure size is correct after changing memcpy (also check for size at end)
rapdulen=sizeof(rapdu);
if (CardTransmit(pnd, capdu, capdulen, rapdu, &rapdulen) < 0){
printf("card trasnmit smaller than 0");
}
hexDump("rapdu: ", rapdu, rapdulen);
printf("closing!\n");
printf("\n");
nfc_close(pnd);
nfc_exit(context);
exit(EXIT_SUCCESS);
}
@GottemHams
Copy link

Unfortunately I would NOT recommend this card, it is read fine by my phone and the PN532 reader.
But the reader where it was meant for, couldn't read the card.

I have the same problem, the device I'm trying to clone a tag for doesn't even respond with at least an error. Did you manage to find affordable gen1 or gen2 7-byte magic cards? All I ever see is 4-byte and this gen3 7-byte, or they're gen2 7-byte but about 20 bucks a piece. ;_; One such card is more expensive than the whole cloner.

@rickdoesburg
Copy link

I've bought this card (not from the Piswords store) but some other vendor. I am able to change the UID using the Flipper Zero CLI and apdu commands but unable to change block 0. I tried cloning my EV-Charge card and it does work on 80% of the chargers. Even though block0 is random.

@koenieee
Copy link
Author

After two years I bought a proxmark3, then I wrote the correct data on the keychain. Now the keychain is the same as the original card, but still the reader doesn't read it.
I guess it has something the original card having a Tag Signature of NXP (certificated).

@koenieee
Copy link
Author

I bricked one of the keychains, not sure if it had anything to do with the code I wrote two years ago...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment