ikarus23 / MifareClassicTool

An Android NFC app for reading, writing, analyzing, etc. MIFARE Classic RFID tags.
http://www.icaria.de/mct/
GNU General Public License v3.0
4.67k stars 900 forks source link

Improve Mifare Classic support check algorithm #45

Closed ikarus23 closed 9 years ago

ikarus23 commented 9 years ago

domints said:

But I've found simple way to check which controller is used by phone - just run command adb shell ls /system/lib/libnfc* And if there's file with bcm or brcm in name it uses Broadcomm chip. For NXP's (compatible) chip I think there should be file with "pn" in name.

domints also said:

I think you could look for file /system/lib/libnfc-nxp.so , but I'm not sure. I'm sure, that for broadcomm chip in /dev folder there's file called bcm2079x, and for NXP's pn544 (I think the most popular if not the only used in android NXP's chip) there's file called pn544

ikarus23 commented 9 years ago

Output of a device with Mifare Classic support (Nexus 7 2012):

shell@grouper:/ $ ls /system/lib/libnfc*                                       
/system/lib/libnfc.so
/system/lib/libnfc_jni.so
/system/lib/libnfc_ndef.so
ikarus23 commented 9 years ago

Regarding the second approach: Checking /dev/* is not possible without root...

ikarus23 commented 9 years ago

Another approach:

try {
    Class.forName( "android.nfc.tech.MifareClassic" );
} catch( ClassNotFoundException e ) {
    // Class not found. Devices does not support Mifare Classic.
}

Devices with no MFC support should have no MifareClassic class.

ikarus23 commented 9 years ago

Here is a testing version of MCT that will check for the class android.nfc.tech.MifareClassic on startup. For devices with no Mifare Classic support there should be an error message saying "This device does not support Mifare Classic!".

domints commented 9 years ago

Umm... Output of mine /system/lib is:

D:\lg\f60\system>adb shell ls /system/lib/libnfc*
/system/lib/libnfc-brcm-nci.so
/system/lib/libnfc_ndef.so

And I have non-rooted LG F60, and from ADB I can ls /dev/ directory without problem... And from Terminal Emulator too.

ikarus23 commented 9 years ago

Hmm... I have a rooted Nexus 7 (2012) but without root I can't read /dev. Not via adb and not via Terminal Emulator. I think I will first check for the class and than fallback to the /system/lib/libnfc*. It should be enough, even without checking /dev.

Have you tried the testing version of MCT from my previous post?

domints commented 9 years ago

Hmm weird thing...

And I forgot to write, I've tried, but didn't get any message about incompability on start.

ikarus23 commented 9 years ago

Now I get it. There has been another misunderstanding. You thought your device has no MifareClassic class because the TagInfo app displayed no "MifareClassic" in "Target technology classes". What this app does is to show the result of Tag.getTechList(). In this list you can see all the technologies the tag and the phone share. But if MifareClassic is not in that list, it does not mean your device don't has this class. It just means that your device and the tag can not communicate via MifareClassic.

In other words: it is pretty much useless to check for the existence of the MifareClassic class.

I created another test version of MCT that checks the /system/lib/libnfc* for libs that have "brcm" in its name. Please try it.

domints commented 9 years ago

Yup, now I correctly get warning that your app is something like useless for my phone :) I think it will be good enough to just check for this libs, and if there will be any other chip that doesn't support mifare you'll just add its driver to checklist :) And just add LG F60, G2 mini and G3s to incompability list in readme - my friends have these and I've checked that they have Broadcomm too :)

ikarus23 commented 9 years ago

Thank you very much! I will update the incompatibility list and close this issue at the time the new MFC support check algorithm is pushed.

ikarus23 commented 9 years ago

Done. See: 3d752037efada14e885029efef518896345078b6

kirelagin commented 9 years ago

This is Nexus 4 with CyanogenMod:

shell@mako:/ $ ls /system/lib/libnfc*
/system/lib/libnfc-nci.so
/system/lib/libnfc_nci_jni.so
/system/lib/libnfc_ndef.so

Also I can list /dev even without root permissions.

shell@mako:/ $ ls /dev/bcm*
/dev/bcm2079x-i2c

So, I think, it does make sense to try to list /dev and then fall back to the /system/lib method if there are not enough permissions.

ikarus23 commented 9 years ago

Thanks for the info.

So, I think, it does make sense to try to list /dev and then fall back to the /system/lib method if there are not enough permissions.

Yep, in this case it makes perfectly sens. I will alter the support check algorithm accordingly.

ikarus23 commented 9 years ago

@kirelagin By reading the output you provided I get MCT didn't complain at startup about something like "no Mifare Classic support", right? Pleas try this testing verstion. It should detect your bcm2079x Broadcom device and display an error message.

kirelagin commented 9 years ago

I didn’t have a chance to test your code on the device, but I believe it won’t work.

The reason is that you are calling isFile while looking for the device node and the question you ask in the comment, whether devices are files, is a valid one. I believe, the answer is “no”. The reason is that isFile in libcore is implemented as:

return S_ISREG(Libcore.os.stat(path).st_mode);

and S_ISREG, in turn, is implemented as:

return (mode & S_IFMT) == S_IFREG;

According to POSIX, this means “regular file”. I’m not sure what is the exact definition of “regular”, but, well, let’s assume it is common knowledge that device nodes are special files, not regular. Indeed, isFile of /dev/sda on my laptop returns false.

kirelagin commented 9 years ago

I don’t think there is a standard way to detect device nodes in Java, but I think you can copy the isFile implementation. Here it is for reference:

public boolean isFile() {
    try {
        return S_ISREG(Libcore.os.stat(path).st_mode);
    } catch (ErrnoException errnoException) {
        // The RI returns false on error. (Even for errors like EACCES or ELOOP.)
        return false;
    }
}

You are looking for S_ISBLK or S_ISCHR, I’m not sure which one, I’ll let you know when I get back to my Nexus (but you can probably check on your phone with NFC).

kirelagin commented 9 years ago

And the last thing. I believe you should wrap both checks (for the device and for the lib) in a try/catch in case listFiles denies access, right?

kirelagin commented 9 years ago

You are looking for S_ISBLK or S_ISCHR

Got my hands on the Nexus. It’s a character device, so S_ISCHR.

ikarus23 commented 9 years ago

Thank you for providing me with all this information, but before I will use the isFile() version that checks for character devices I would like you to test the MCT version as it is. They java spec says:

Tests whether the file denoted by this abstract pathname is a normal file. A file is normal if it is not a directory and, in addition, satisfies other system-dependent criteria.

Maybe character devices do "satisfie other system-dependent criteria". I this case is would prefer the normal isFile() method because it is more readable.

Surrounding everything with a try/catch sounds like good idea (even though on my nexus 7, where the access to /dev is denied, it just returned null without throwing any exception).

ikarus23 commented 9 years ago

A friend of mine has a Nexus 4 with Cyanogenmod too. But on his device he can't list /dev/bcm* without root:

shell@mako:/ $ ls -la /dev/bcm*
/dev/bcm*: No such file or directory

This is because the device belongs to nfc. Other users have no permissions at all:

root@mako:/dev # ls -la bcm*                                                 
crw-rw---- nfc      nfc       10,  92 2015-04-04 17:49 bcm2079x-i2c

The build he is running is 12-20150325 nightly What are you using? Who is the owner of this device? Which group does it belong to?

kirelagin commented 9 years ago

Come on, permissions of a file have nothing to do with your ability to see it in the listing. The r permission on the directory containing the file is what matters.

shell@mako:/ $ ls -ld /dev
drwxr-xr-x root     root              2015-04-22 23:17 dev

It’s CM11 20140919 nightly.

I checked on my own phone with CM12 20150217 nightly and it still has r:

shell@ville:/ $ ls -ld /dev
drwxr-xr-x root     root              2015-04-22 17:31 dev

So, that’s strange. Either they changed this during March or something’s special about your friend‘s phone.

Anyway, you’ll be able to check if a file with a known name exists (as opposed to listing the directory) even if the directory doesn’t have r for your user, but has x. So if /dev on your friend’s Nexus has x for other users, checking for existence of /dev/bcm2079x-i2c will work as expected. The problem here is that in this case you’ll be checking for the file with the fixed name, not for bcm*. But you can do this progressively:

domints commented 9 years ago

/dev directory isn't readable for everyone on every phone. It depends a lot on the manufacturer. But after, or before checks that @kirelagin suggested, you could check for nxp-related files. Especially libnfc-nxp, and some files in /dev, but I don't know their names (I have bcm based phone). But when you find NXP-related files, you can be sure there's Mifare support in this phone. Another way could be ls /sys/module, because if NFC is in use, there should be some kernel module loaded, but to be onest I'm not able to find one in my output.

ikarus23 commented 9 years ago

@kirelagin You are absolutely right about the permission thing. My bad.

  • check if /dev/bcm2079x-i2c (and maybe other known names) exists. If yes, then fail, else
  • try to list /dev/bcm*. If found something, then fail, else
  • try to find libnfc-brcm*. If found, then fail, else
  • you don't know

Sounds like a pretty solid solution. I will do that, but maybe without the /dev/bcm*. Broadcom produces also other products like wifi chips. I'm not sure if there are other devices by Broadcom in /dev starting with bcm* which are not NFC controllers.

@domints Are you sure about the libnfc-nxp? I know this is the name of the library for NFC chips by NXP on Android. But my Nexus 7 (2012) only shows:

shell@grouper:/ $ ls /system/lib/libnfc*                                       
/system/lib/libnfc.so
/system/lib/libnfc_jni.so
/system/lib/libnfc_ndef.so

I searched the whole device for files with *nxp* in their name. I found none. The only other NXP related file i know of is /dev/pn544. But this is a device again...

ikarus23 commented 9 years ago

This is a new testing version of MCT which directly checks for /dev/bcm2079-i2c.

File device = new File("/dev/bcm2079x-i2c");
if (device.exists()) {
    ...
}

Please try it.

kirelagin commented 9 years ago

Yep, got the warning on my Nexus 4 with CM.

iceman1001 commented 9 years ago

What about broaden your search?
if % is jokersign, the search for devices starting with "/dev/bcm2790%" ??

ikarus23 commented 9 years ago

@kirelagin Perfect, thank you!

@iceman1001 For doing something like this directory listing is needed which requires the r and the x permission. Probing for the full path/name only requires the x permission. The permissions on /dev are very rare on Android. Therefore I chose the approach which works with less permissions.

iceman1001 commented 9 years ago

I see, thanks for explaining it.

ikarus23 commented 9 years ago

The changes are now in the master branch. See: https://github.com/ikarus23/MifareClassicTool/commit/44cf28d172c5fb888de932073865a062fbf64db9