HiddenRamblings / TagMo

GNU General Public License v3.0
2.98k stars 385 forks source link

[Feature]: The Flipper-Zero nfc files should contain the correct password so that they can be run directly on the Flipper-Zero. #732

Closed danizwam closed 4 months ago

danizwam commented 4 months ago

Feature Explanation

For the first time in months, I've been working with Amiibos on the Flipper-Zero and today I realized that NFC files now have to contain a password in order to be correctly emulated by the Flipper-Zero and recognized by the Switch.

To be able to use the NFC files generated by TagMo on the Flipper-Zero, there are various options. There are several ways to convert them directly on the Flipper-Zero, but all of them are very complicated and require either the Switch and its NFC reader or a real Amiibo. If the password found is incorrect, there may be problems. If you use an external Amiibo and its password, the NFC is encrypted with it (but this ensures that the NFC Amiibo is no longer unique). Another option is to use an external tool to convert the NFC on the PC. I found the last option to be the easiest and it always worked perfectly.

The password for the Amiibos is generated from the 7 bytes of the UID and stored in page 133. A default value is written to page 134.

On this repository https://github.com/turbospok/Flipper-NTAG215-password-converter there is a Python script that can be used to convert the NFC files so that they run on the Flipper-Zero.

I was able to convert the NFC files generated by TagMo with this tool. It would be great if the NFC files generated by TagMo contained the password and could be run directly on the Flipper-Zero.

Usage Confirmation

danizwam commented 4 months ago

I would also like to mention that Flipper-Zero converts the .nfc files into a .shd file if they are to be emulated. The file has a similar structure to an .nfc file, but contains a different header and the individual pages have different hex values. Something is therefore converted there.

I just found something about the .shd Files:

What are the .shd files in the NFC directory?

These are shadow files, and they're created whenever an emulated tag is written to. They store a copy of the original file with whatever was written. This way, the original file remains untouched.

I think they do not matter to this feature ;)

AbandonedCart commented 4 months ago

Looks like it's the second version of their format, so the real question is if adding the password to everything will break the first or if I need to also check for the version.

danizwam commented 4 months ago

My experience is that a valid NFC file (i.e. one with a password) is sufficient. The .shd file is generated on the fly. I assume that you don't need to worry about the .shd files.

AbandonedCart commented 4 months ago

I meant there are apparently two iterations of the flipper firmware.

danizwam commented 4 months ago

I meant there are apparently two iterations of the flipper firmware.

Take a look at this Issue, where it's discussed.

https://github.com/flipperdevices/flipperzero-firmware/issues/3188

They are mentioning that adding the password to 133 and 134 is a workaround, but i did not get if it`s a lasting patch, or really just a workaround.

Edit: Since the Pages 133 and 134 where all 00 00 00 00 in my previous nfc files, i don`t think, that adding values to 133 und 134 would break it for older firmwares.

AbandonedCart commented 4 months ago

I should have this fixed tonight. I added a note with the changes, but want to verify before publishing with everything being live.

AbandonedCart commented 4 months ago

Hopefully I did that right. First 3 bytes of page 0, all of page 4 as the uid. Xor a bunch of stuff and write to page 133. Inject predefined values in page 134.

danizwam commented 4 months ago

Good morning ;)

I updated to the latest version and tried to export a nfc file. Unfortunately, te pages 133 and 134 are missing.

The log also looks weird:

TagMo 4.1.8 (GitHub Release) #cdc00b0 OnePlus TIRAMISU (13) - 10.00 GB RAM --------- beginning of main05-09 11:22:26.539  9130  9130 E .eightbit:TagMo: Unknown bits set in runtime_flags: 0x4000000005-09 11:22:26.542  9130  9130 E .eightbit:TagMo: Not starting debugger since process cannot load the jdwp agent.05-09 11:22:26.605  9130  9130 W libc    : Access denied finding property "ro.odm.prev.product.name"05-09 11:22:26.649  9130  9130 E .eightbit:TagMo: sysOpen failed with error=Permission denied05-09 11:22:26.678  9130  9895 E .eightbit:TagMo: Loaded layer handle (12925507548731984523) for layer /my_product/lib64/libcolorx-loader.so05-09 11:22:26.678  9130  9895 E .eightbit:TagMo: Looking for entrypoint ColorX_Check05-09 11:22:26.678  9130  9895 E .eightbit:TagMo: Found ColorX_Check for /my_product/lib64/libcolorx-loader.so05-09 11:22:26.678  9130  9895 E .eightbit:TagMo: Check failed--------- beginning of system05-09 11:22:26.765  9130  9130 E OplusCustomizeRestrictionManager: sInstance is null, start a new sInstance05-09 11:22:27.091  9130  9887 W Parcel  : Expecting binder but got null!05-09 11:22:29.468  9130  9887 W Parcel  : Expecting binder but got null!05-09 11:22:31.117  9130  9887 W Parcel  : Expecting binder but got null!05-09 11:22:34.154  9130  9130 E IPCThreadState: attemptIncStrongHandle(83): Not supported05-09 11:22:34.155  9130  9130 E IPCThreadState: attemptIncStrongHandle(85): Not supported05-09 11:22:34.202  9130  9887 W Parcel  : Expecting binder but got null!

danizwam commented 4 months ago

I pulled the latest version from github and inspected the code and there are two issues:

  1. you are not appending the calculated string to the contents. I did this locally and the pages 133 and 134 were included:

                pages[1]?.let { pages[0]?.copyOf(3)?.plus(it) }?.let { uid ->
                    val result = "Page $index: ${byteArrayOf(
                        uid[1] xor uid[3] xor 0xAA.toByte(),
                        uid[2] xor uid[4] xor 0x55,
                        uid[3] xor uid[5] xor 0xAA.toByte(),
                        uid[4] xor uid[6] xor 0x55).toHex().hexFormat}"
                    contents.append(Debug.separator).append(result)
                }
            }
            134 -> {
                val result = "Page $index: ${byteArrayOf(0x80.toByte(), 0x80.toByte(), 0, 0).toHex().hexFormat}"
                contents.append(Debug.separator).append(result)
            }
  2. the calculated password in page 133 is not correct. i took your generated nfc and recalculated the password with the mentioned python script.

Example:

For the UID: 04 DF 89 DA 63 46 11, the correct password is: AF BF 36 27 (and the switch recognized it) You calculated: 16 9A D8 4E (the switch shows an error).

I don't know enough about Kotlin and haven't had time to take a closer look at the algorithm yet

danizwam commented 4 months ago

I converted the Python script to Java and i got the same results when calculating the password.

Then i debuged parallel the same Amiibo and found out, that you are using the wrong UID. I don´t know what is wrong, but i know something is wrong:

Example:

The UID byteArray used to calculate in your Flipper.kt:

grafik

The UID from the same Amiibo in Java:

grafik

Now back to you Flipper.kt. If i use the previous uidHex, you are appending in line 39, and evaluate it as HexByteArray, i get the same UID as in Java:

grafik

I did not fully understand how you find the uid in line 54 yet, but something is wrong ;)

danizwam commented 4 months ago

OK, last edit ;)

There is one missing byte in the middle (4th position). If you change copyOf(3) to copyOf(4), the UID is the same as in java and the generated password is correct:

pages[1]?.let { pages[0]?.copyOf(4)?.plus(it) }?.let { uid ->

That was fun ;)

AbandonedCart commented 4 months ago

Their documentation said it was 3 bytes from page 0, all 4 from page 1. That sounds like just pages 3 and 4.

Looks like my two ideas got combined on the write. One was to only change the content and the other was to write the whole line fresh. I think only the content will be the most efficient, though.

AbandonedCart commented 4 months ago

If you can't already tell, I don't actually have a Flipper to verify any of this.

AbandonedCart commented 4 months ago

Just so it makes sense if you read through the code, the reason I dropped the copy entirely is a page is 4 bytes, so copyOf(4) is essentially just the entire page.

Also, thanks for sponsoring! I just got the notification.

danizwam commented 4 months ago

Just so it makes sense if you read through the code, the reason I dropped the copy entirely is a page is 4 bytes, so copyOf(4) is essentially just the entire page.

Also, thanks for sponsoring! I just got the notification.

You are welcome ;)

btw: i just tested it and it works perfectly ;)

AbandonedCart commented 4 months ago

Awesome. Thanks. Let me know if you run into any other issues.