├── .gitignore ├── .gitmodules ├── Makefile ├── pimiibo.cpp ├── amiibo.h ├── nfchandler.h ├── amiitool.h ├── amiibo.cpp ├── amiitool.cpp ├── nfchandler.cpp └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | pimiibo 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "amiitool"] 2 | path = amiitool 3 | url = https://github.com/socram8888/amiitool.git 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES = amiibo.cpp amiitool.cpp nfchandler.cpp pimiibo.cpp 2 | 3 | all: pimiibo amiitoolsubmodule 4 | 5 | pimiibo: $(SOURCES) 6 | $(CXX) $(CXXFlags) $(SOURCES) -o pimiibo -lnfc 7 | 8 | amiitoolsubmodule: 9 | cd amiitool && $(MAKE) amiitool 10 | 11 | clean: 12 | rm pimiibo 13 | cd amiitool && $(MAKE) clean 14 | -------------------------------------------------------------------------------- /pimiibo.cpp: -------------------------------------------------------------------------------- 1 | #include "amiibo.h" 2 | #include "amiitool.h" 3 | #include "nfchandler.h" 4 | 5 | #include 6 | 7 | void printUsage() { 8 | printf("pimiibo keyfile binfile\n"); 9 | } 10 | 11 | int main(int argc, char** argv) { 12 | if (argc != 3) { 13 | fprintf(stderr, "Incorrect number of arguments\n"); 14 | printUsage(); 15 | exit(1); 16 | } 17 | 18 | Amiitool::setKeyPath(argv[1]); 19 | 20 | Amiibo *amiibo = new Amiibo(argv[2]); 21 | NFCHandler *nfc = new NFCHandler(); 22 | 23 | nfc->writeAmiibo(amiibo); 24 | } 25 | -------------------------------------------------------------------------------- /amiibo.h: -------------------------------------------------------------------------------- 1 | #define AMIIBO_SIZE 540 2 | 3 | #include 4 | #include 5 | 6 | class Amiibo { 7 | public: 8 | Amiibo(const char *filePath); 9 | 10 | void setUUID(const uint8_t uuid[]); 11 | 12 | uint8_t encryptedBuffer[AMIIBO_SIZE]; 13 | 14 | private: 15 | uint8_t buffer[AMIIBO_SIZE]; 16 | 17 | void readFileIntoBuffer(const char *filePath, uint8_t *buffer, size_t size); 18 | 19 | void replaceWithUUID(const uint8_t uuid[]); 20 | void replacePassword(const uint8_t uuid[]); 21 | void setDefaults(const uint8_t uuid[]); 22 | }; 23 | -------------------------------------------------------------------------------- /nfchandler.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define UUID_SIZE 7 5 | 6 | class Amiibo; 7 | 8 | class NFCHandler { 9 | public: 10 | NFCHandler(); 11 | void readTagUUID(uint8_t uuidBuffer[]); 12 | void writeAmiibo(Amiibo *amiibo); 13 | 14 | private: 15 | nfc_target target; 16 | nfc_context *context; 17 | nfc_device *device; 18 | 19 | void writeBuffer(const uint8_t buffer[]); 20 | void writePage(uint8_t page, const uint8_t buffer[]); 21 | void writeDataPages(const uint8_t buffer[]); 22 | void writeDynamicLockBytes(); 23 | void writeStaticLockBytes(); 24 | }; 25 | -------------------------------------------------------------------------------- /amiitool.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | class Amiitool { 5 | public: 6 | Amiitool(); 7 | int decryptBuffer(uint8_t encryptedBuffer[], uint8_t *decryptedBuffer); 8 | int encryptBuffer(uint8_t encryptedBuffer[], uint8_t *decryptedBuffer); 9 | 10 | static Amiitool *shared(); 11 | static void setKeyPath(const char *keyPath); 12 | 13 | void printHex(const uint8_t *buffer, const size_t size); 14 | 15 | private: 16 | int writePipe[2]; 17 | int readPipe[2]; 18 | int savedStdin; 19 | int savedStdout; 20 | 21 | static Amiitool *_shared; 22 | static const char *_keyPath; 23 | 24 | int pipeToAmiitool(const char *args, const char* keyPath, uint8_t *inputBuffer, uint8_t *outputBuffer); 25 | void redirectIO(); 26 | void resetIO(); 27 | }; 28 | -------------------------------------------------------------------------------- /amiibo.cpp: -------------------------------------------------------------------------------- 1 | #include "amiibo.h" 2 | #include "amiitool.h" 3 | 4 | #include 5 | 6 | #define UUID_OFFSET 468 7 | #define PASSWORD_OFFSET 532 8 | #define PASSWORD_SIZE 4 9 | 10 | Amiibo::Amiibo(const char* filePath) { 11 | readFileIntoBuffer(filePath, encryptedBuffer, AMIIBO_SIZE); 12 | 13 | Amiitool::shared()->decryptBuffer(encryptedBuffer, buffer); 14 | } 15 | 16 | void Amiibo::setUUID(const uint8_t *uuid) { 17 | printf("\nUpdating bin for new UID:\n"); 18 | 19 | // Credit: https://gist.githubusercontent.com/ShoGinn/d27a726296f4370bbff0f9b1a7847b85/raw/aeb425e8b1708e1c61f78c3e861dad03c20ca8ab/Arduino_amiibo_tool.bash 20 | replaceWithUUID(uuid); 21 | replacePassword(uuid); 22 | setDefaults(uuid); 23 | Amiitool::shared()->encryptBuffer(encryptedBuffer, buffer); 24 | 25 | printf("Finished updating bin\n\n"); 26 | } 27 | 28 | void Amiibo::readFileIntoBuffer(const char *filePath, uint8_t *buffer, size_t size) { 29 | FILE *file = fopen(filePath, "r"); 30 | 31 | if (!file) { 32 | fprintf(stderr, "Could not open %s\n", filePath); 33 | exit(1); 34 | } 35 | 36 | if (size != fread(buffer, 1, size, file)) { 37 | fprintf(stderr, "Read incorrect number of bytes from file: %s\n", filePath); 38 | exit(1); 39 | } 40 | 41 | fclose(file); 42 | } 43 | 44 | void Amiibo::replaceWithUUID(const uint8_t uuid[]) { 45 | uint8_t bcc[2]; 46 | 47 | printf("Replacing UID\n"); 48 | bcc[0] = 0x88 ^ uuid[0] ^ uuid[1] ^ uuid[2]; 49 | bcc[1] = uuid[3] ^ uuid[4] ^ uuid[5] ^ uuid[6]; 50 | 51 | int i; 52 | for (i = 0; i < 3; i++) { 53 | buffer[UUID_OFFSET + i] = uuid[i]; 54 | } 55 | 56 | buffer[UUID_OFFSET + i++] = bcc[0]; 57 | 58 | for (; i < 8; i++) { 59 | buffer[UUID_OFFSET + i] = uuid[i - 1]; 60 | } 61 | } 62 | 63 | void Amiibo::replacePassword(const uint8_t uuid[]) { 64 | uint8_t password[PASSWORD_SIZE] = {0, 0, 0, 0}; 65 | 66 | printf("Updating password\n"); 67 | password[0] = 0xAA ^ uuid[1] ^ uuid[3]; 68 | password[1] = 0x55 ^ uuid[2] ^ uuid[4]; 69 | password[2] = 0xAA ^ uuid[3] ^ uuid[5]; 70 | password[3] = 0x55 ^ uuid[4] ^ uuid[6]; 71 | 72 | for (int i = 0; i < PASSWORD_SIZE; i++) { 73 | buffer[PASSWORD_OFFSET + i] = password[i]; 74 | } 75 | } 76 | 77 | void Amiibo::setDefaults(const uint8_t uuid[]) { 78 | printf("Writing magic bytes\n"); 79 | 80 | // Same as bcc[1] 81 | buffer[0] = uuid[3] ^ uuid[4] ^ uuid[5] ^ uuid[6]; 82 | 83 | // All of these are magic values 84 | buffer[536] = 0x80; 85 | buffer[537] = 0x80; 86 | 87 | buffer[520] = 0; 88 | buffer[521] = 0; 89 | buffer[522] = 0; 90 | 91 | buffer[2] = 0; 92 | buffer[3] = 0; 93 | } 94 | -------------------------------------------------------------------------------- /amiitool.cpp: -------------------------------------------------------------------------------- 1 | #include "amiitool.h" 2 | #include "amiibo.h" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define COMMAND_SIZE 1000 9 | 10 | Amiitool *Amiitool::_shared = NULL; 11 | const char *Amiitool::_keyPath = NULL; 12 | 13 | Amiitool *Amiitool::shared() { 14 | if (!_shared) { _shared = new Amiitool(); } 15 | 16 | return _shared; 17 | } 18 | 19 | void Amiitool::setKeyPath(const char *keyPath) { 20 | Amiitool::_keyPath = keyPath; 21 | } 22 | 23 | Amiitool::Amiitool() { 24 | if (pipe(writePipe) < 0) { 25 | fprintf(stderr, "Could not open write pipe\n"); 26 | exit(1); 27 | } 28 | 29 | if (pipe(readPipe) < 0) { 30 | fprintf(stderr, "Could not open read pipe\n"); 31 | exit(1); 32 | } 33 | 34 | savedStdin = dup(0); 35 | savedStdout = dup(1); 36 | } 37 | 38 | int Amiitool::decryptBuffer(uint8_t *encryptedBuffer, uint8_t *decryptedBuffer) { 39 | printf("\nDecrypting bin\n"); 40 | return pipeToAmiitool("-d", Amiitool::_keyPath, encryptedBuffer, decryptedBuffer); 41 | } 42 | 43 | int Amiitool::encryptBuffer(uint8_t *encryptedBuffer, uint8_t *decryptedBuffer) { 44 | printf("Encrypting\n"); 45 | return pipeToAmiitool("-e", Amiitool::_keyPath, decryptedBuffer, encryptedBuffer); 46 | } 47 | 48 | int Amiitool::pipeToAmiitool(const char *args, const char* keyPath, uint8_t *inputBuffer, uint8_t *outputBuffer) { 49 | printf("Sending bin to amiitool..."); 50 | 51 | redirectIO(); 52 | 53 | int pipeSize; 54 | if (AMIIBO_SIZE != (pipeSize = write(writePipe[1], inputBuffer, AMIIBO_SIZE))) { 55 | fprintf(stderr, "Wrote incorrect size to pipe: %d\n", pipeSize); 56 | perror("write"); 57 | exit(1); 58 | } 59 | 60 | const char *staticCommand = "./amiitool/amiitool %s -k %s"; 61 | char command[COMMAND_SIZE + strlen(staticCommand)]; 62 | 63 | if (strlen(keyPath) >= COMMAND_SIZE) { 64 | fprintf(stderr, "Key path too big\n"); 65 | exit(1); 66 | } 67 | 68 | sprintf(command, staticCommand, args, keyPath); 69 | system(command); 70 | 71 | if (AMIIBO_SIZE != (pipeSize = read(readPipe[0], outputBuffer, AMIIBO_SIZE))) { 72 | fprintf(stderr, "Read incorrect size from pipe: %d\n", pipeSize); 73 | exit(1); 74 | } 75 | 76 | resetIO(); 77 | 78 | printf("Done\n"); 79 | return 0; 80 | } 81 | 82 | void Amiitool::redirectIO() { 83 | if (dup2(writePipe[0], 0) < 0) { 84 | fprintf(stderr, "Could not redirect stdin\n"); 85 | exit(1); 86 | } 87 | 88 | if (dup2(readPipe[1], 1) < 0) { 89 | fprintf(stderr, "Could not redirect stdout\n"); 90 | exit(1); 91 | } 92 | } 93 | 94 | void Amiitool::resetIO() { 95 | if (dup2(savedStdin, 0) < 0) { 96 | fprintf(stderr, "Could not reset stdin\n"); 97 | exit(1); 98 | } 99 | 100 | if (dup2(savedStdout, 1) < 0) { 101 | fprintf(stderr, "Could not reset stdout\n"); 102 | exit(1); 103 | } 104 | } 105 | 106 | void Amiitool::printHex(const uint8_t *buffer, const size_t size) { 107 | size_t i; 108 | 109 | for (i = 0; i < size; i++) { 110 | printf("%02x ", buffer[i]); 111 | } 112 | printf("\n"); 113 | } 114 | -------------------------------------------------------------------------------- /nfchandler.cpp: -------------------------------------------------------------------------------- 1 | #include "nfchandler.h" 2 | #include "amiitool.h" 3 | #include "amiibo.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define PAGE_COUNT 135 10 | #define WRITE_COMMAND 0XA2 11 | 12 | const uint8_t dynamicLockBytes[4] = { 0x01, 0x00, 0x0f, 0xbd }; 13 | const uint8_t staticLockBytes[4] = { 0x00, 0x00, 0x0F, 0xE0 }; 14 | 15 | const nfc_modulation nmMifare = { 16 | .nmt = NMT_ISO14443A, 17 | .nbr = NBR_106, 18 | }; 19 | 20 | NFCHandler::NFCHandler() { 21 | printf("Initializing NFC adapter\n"); 22 | nfc_init(&context); 23 | 24 | if (!context) { 25 | printf("Unable to init libnfc (malloc)\n"); 26 | exit(1); 27 | } 28 | 29 | device = nfc_open(context, NULL); 30 | 31 | if (device == NULL) { 32 | printf("ERROR: %s\n", "Unable to open NFC device."); 33 | exit(1); 34 | } 35 | 36 | if (nfc_initiator_init(device) < 0) { 37 | nfc_perror(device, "nfc_initiator_init"); 38 | exit(1); 39 | } 40 | 41 | printf("NFC reader: opened\n"); 42 | } 43 | 44 | void NFCHandler::readTagUUID(uint8_t uuidBuffer[]) { 45 | printf("***Scan tag***\n"); 46 | 47 | if (nfc_initiator_select_passive_target(device, nmMifare, NULL, 0, &target) > 0) { 48 | printf("Read UID: "); 49 | int uidSize = target.nti.nai.szUidLen; 50 | Amiitool::shared()->printHex(target.nti.nai.abtUid, uidSize); 51 | 52 | if (UUID_SIZE != uidSize) { 53 | fprintf(stderr, "Read wrong size UID\n"); 54 | exit(1); 55 | } 56 | 57 | for (int i = 0; i < UUID_SIZE; i++) { 58 | uuidBuffer[i] = target.nti.nai.abtUid[i]; 59 | } 60 | } 61 | } 62 | 63 | void NFCHandler::writeAmiibo(Amiibo *amiibo) { 64 | uint8_t uuid[UUID_SIZE]; 65 | readTagUUID(uuid); 66 | 67 | amiibo->setUUID(uuid); 68 | writeBuffer(amiibo->encryptedBuffer); 69 | } 70 | 71 | void NFCHandler::writeBuffer(const uint8_t *buffer) { 72 | printf("Writing tag:\n"); 73 | writeDataPages(buffer); 74 | writeDynamicLockBytes(); 75 | writeStaticLockBytes(); 76 | printf("Finished writing tag\n"); 77 | } 78 | 79 | void NFCHandler::writeDataPages(const uint8_t *buffer) { 80 | printf("Writing encrypted bin:\n"); 81 | for (uint8_t i = 3; i < PAGE_COUNT; i++) { 82 | writePage(i, buffer + (i * 4)); 83 | } 84 | printf("Done\n"); 85 | } 86 | 87 | void NFCHandler::writeDynamicLockBytes() { 88 | printf("Writing dynamic lock bytes\n"); 89 | writePage(130, dynamicLockBytes); 90 | printf("Done\n"); 91 | } 92 | 93 | void NFCHandler::writeStaticLockBytes() { 94 | printf("Writing static lock bytes\n"); 95 | writePage(2, staticLockBytes); 96 | printf("Done\n"); 97 | } 98 | 99 | void NFCHandler::writePage(uint8_t page, const uint8_t *buffer) { 100 | printf("Writing to %d: %02x %02x %02x %02x...", 101 | page, buffer[0], buffer[1], buffer[2], buffer[3]); 102 | 103 | uint8_t sendData[6] = { 104 | WRITE_COMMAND, page, buffer[0], buffer[1], buffer[2], buffer[3] 105 | }; 106 | 107 | int responseCode = nfc_initiator_transceive_bytes(device, sendData, 6, NULL, 0, 0); 108 | 109 | if (responseCode == 0) { 110 | printf("Done\n"); 111 | } else { 112 | printf("Failed\n"); 113 | fprintf(stderr, "Failed to write to tag\n"); 114 | nfc_perror(device, "Write"); 115 | exit(1); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # pimiibo 2 | 3 | This is a tool for spoofing amiibo NFC tags using a Raspberry Pi and PN532 NFC reader/writer with [libnfc](http://nfc-tools.org/index.php/Libnfc). Enjoy and please let me know if you have any issues :) 4 | 5 | ## Hardware 6 | This project requires 3 main components: a Pi, a PN532 NFC reader/writer, and NTAG 215 NFC tags. You *must* use NTAG 215 tags. Any other tag type (e.g NTAG 210, 216, etc.) **will not work**. Anything meeting these requirements should be fine, but here is my setup for reference: 7 | 8 | 1. Raspberry Pi Zero W ($10). A Pi Zero (not W) would cost $5. 9 | 10 | 2. Elechouse PN532 NFC Module v3 ($10): [Amazon](https://smile.amazon.com/gp/product/B01NBSW0NU/). 11 | 12 | 3. NTAG 215 tags. I've personally tested with [these](https://smile.amazon.com/gp/product/B078WMQPCZ/) ($10 for 11) and [these](https://smile.amazon.com/gp/product/B0759W25TL/) ($28 for 60). 13 | 14 | ## Software setup 15 | 16 | 1. Clone this repository with submodules: 17 | 18 | `git clone --recurse-submodules https://github.com/garrett-davidson/pimiibo.git` 19 | 20 | 2. Install `libnfc-dev`: 21 | 22 | `sudo apt-get update && sudo apt-get install libnfc-dev` 23 | 24 | 3. Compile sources. This will probably take a minute or two the first time on a Pi. 25 | 26 | `make` 27 | 28 | 29 | ## Set up I2C (Optional) 30 | This section is dependent on your NFC board and how plan to communicate with it. If you plan to use I2C, you need to enable I2C on your Pi and tell `libnfc` how to find it. If you plan to use SPI, HSU, or other communication methods, follow the specific instructions for your board. 31 | 32 | 33 | (**Note for SPI and HSU/UART users**: if you're using a Pi 3 or Pi Zero W with Bluetooth built-in, then you will want to disable Bluetooth when communicating with the board. This is because the serial pin got hijacked for Bluetooth and the software pin has clocking issues. You can read more [here](https://spellfoundry.com/2016/05/29/configuring-gpio-serial-port-raspbian-jessie-including-pi-3/).) 34 | 35 | 1. Enable I2C on your Pi. 36 | 37 | `sudo raspi-config` 38 | 39 | Then navigate to `Interfacing Options` -> `I2C` -> `Yes`. 40 | 41 | 2. Install i2c-tools 42 | 43 | `sudo apt-get install i2c-tools` 44 | 45 | 3. Configure `libnfc` to search for your board: 46 | 47 | `sudo nano /etc/nfc/libnfc.conf` 48 | 49 | Add these lines to the end of the file: 50 | 51 | ``` 52 | device.name = "_PN532_I2c" 53 | device.connstring = "pn532_i2c:/dev/i2c-1" 54 | ``` 55 | 56 | 4. Make sure your board is in I2C mode. Some boards, like the one linked above, support multiple communication modes, which are selected with physical dipswitches. For that Elechouse board, I2C mode is `1 0`. This means toggle the top switch on (to the right), and the bottom switch off (to the left). 57 | 58 | 5. Wire the board. For through-hole boards (including the Pi), I *highly recommend* soldering all of the headers on before continuing. A loose connection on any of these wires can damage your Pi and your NFC board, or waste an NFC tag. Once that is done, wire the board as follows: 59 | 60 | | NFC Board Pin | Pi Pin | 61 | |:-------------:|:-------------:| 62 | | Ground | Ground | 63 | | VCC | 5 V | 64 | | SDA | SDA.1 (Pin 3) | 65 | | SCL | SCL.1 (Pin 5) | 66 | 67 | 68 | 6. Check to make sure it is working. 69 | 70 | `sudo i2cdetect –y 1` 71 | 72 | If all goes well, you should see a device show up as a number on that list (mine is 24). If the list is empty (all dashes), double check your setup. 73 | 74 | ## Getting the required files 75 | After you have followed the above setup, you just need two more files to start making your own amiibo: an amiibo dump, and the key file. 76 | 77 | ### Amiibo Dump 78 | Amiibo dumps are not hard to get. Amiibo are 540 bytes and usually stored in a .bin (binary) file. Assuming you are simply cloning your own legitimate amiibo, you can use any dumping tool to dump it to a .bin file. Otherwise, that's probably copyright infringement or something. 79 | 80 | ### Key file 81 | This is the file containing Nintendo's key, which they use to encrypt/decrypt data on the amiibo. It is probably also copyrighted content, but it's a 160 byte .bin file which matches the MD5 `45fd53569f5765eef9c337bd5172f937`. 82 | 83 | ## Usage 84 | 85 | Start the program: 86 | `./pimiibo path-to-key-file path-to-amiibo-file` 87 | 88 | Once you see `***Scan tag***`, place and hold your blank NFC tag on the reader/writer. You should then see messages scrolling past with each data page as it begins writing them. ***Do not remove your tag until the write is finished.*** When you see `Finished writing tag`, it is safe to remove your tag and enjoy your new amiibo! 89 | 90 | ## Common Problems 91 | 92 | * Failed to initialize adapter 93 | ``` 94 | Initializing NFC adapter 95 | error libnfc.bus.i2c Error: wrote only -1 bytes (10 expected). 96 | ``` 97 | Your Pi could not find your NFC device. Double check your wiring and try again. 98 | 99 | * Failed to write a page 100 | ``` 101 | Writing to 4: a5 b0 d1 00...Failed 102 | Failed to write to tag 103 | Write: RF Transmission Error 104 | ``` 105 | This means your tag is already locked. The NTAG 21x spec declares locking bits which permanently prevent certain parts of a tag from being written to once they are set. Therefore once the locking bits are set, you cannot rewrite this tag to another amiibo. All amiibo are required to have certain locking bits sets, so you cannot change a tag once you've used it. 106 | 107 | If this happened anywhere other than page 4, it probably means that your device lost connection to the tag. Try again while keeping the tag closer to your device. Hopefully in the future I'll add a feature to check which of these problems occurred. 108 | --------------------------------------------------------------------------------