NFC模拟卡功能是工作后的第一个项目,当时完全没接触过android,别说是代码,就连用的手机也不是andorid,所以当时遇到的一些问题现在看起来很蠢,都是一些基础问题。现在回头看实现当时需求的功能,可能1天的时间都用不到。
需求:让手机能当做门禁卡刷开小区、办公楼闸机。
目录
问题1:无论给什么权限的app,都不能修改/vendor/etc/libnfc-nxp_RF.conf
1 NFC基础知识
NFC技术支持设备之间进行非接触式点对点的数据传输,可以在10cm距离内交换数据,属于一种非接触式识别和互联通信技术,通常在移动设备、消费类电子产品、智能硬件工具间进行近距离无线通信。
其工作分为三个模式:
- 点对点模式(P2P mode)
在此模式下,两台支持 NFC 的设备在短距离内直接通信。它用于安全支付、文件共享和设备配对等应用。 - 卡模式(Card emulation)
在此模式下,NFC 设备模拟一张非接触式智能卡。它允许设备与传统非接触式卡读卡器进行通信。例如,NFC 智能手机可以模拟一张交通卡,用于乘坐公共交通工具。以及本文提到的模拟门禁卡。 - 读卡器模式(Reader/writer mode)
在此模式下,NFC 设备充当读卡器或写入器,与非接触式智能卡或标签进行通信。例如,NFC 智能手机可以读取非接触式银行卡或门禁卡上的数据。
这个项目用到的模式就是卡模式和读卡器模式,先通过读卡器模式读取门禁卡上的数据,再写入手机中,然后进入卡模式,手机就可以当做门禁卡使用了。
标签:Tag,其中包含了少量的信息。具有不同的标准,执行不同的协议,不同类型的卡中可能包含不同类型的tag。
2 读取NFC卡内信息
android提供了原生接口实现此功能,使用读卡器模式,通过NFC.Adapter实现对tag等信息的读取。
读取中分为三个优先级:
- NDEF.DISCOVERED
此意图在以下情况下广播:检测到包含 NDEF(NFC 数据交换格式)消息的 NFC 标签或设备。检测到支持 NDEF 的 NFC 设备,但没有包含 NDEF 消息。 - TECH_DISCOVERED
检测到支持特定 NFC 技术的 NFC 标签或设备,无论是否包含 NDEF 消息。 - TAG_DISCOVERED
检测到任何类型的 NFC 标签,无论是否包含 NDEF 消息或支持特定 NFC 技术。
TAG_DISCOVERED
意图的优先级最高,其次是 TECH_DISCOVERED
意图,最后是NDEF.DISCOVERED
意图。这意味着如果同时检测到 NFC 标签和 NDEF 消息,则应用程序将首先收到 TAG_DISCOVERED
意图,然后是 TECH_DISCOVERED
意图,最后是 NDEF.DISCOVERED
意图。
在Manifest中注册意图:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.NFCread">
<activity android:name=".MainActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.nfc.action.TECH_DISCOVERED" />
</intent-filter>
<meta-data
android:name="android.nfc.action.TECH_DISCOVERED"
android:resource="@xml/nfc_tech_filter" />
</activity>
</application>
卡中包含了很多数据,对门禁卡功能有用的是什么呢?
>>>tag中的uid,在非加密门禁卡中,仅比对此项是否与数据库中匹配。
可以理解为卡的“身份证”。
读取uid信息,代码中的cardId变量:
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
String cardId = ByteArrayToHexString(tag.getId());
Ndef ndef = Ndef.get(tag);
if(ndef != null){
msgg = "type:" + ndef.getType();
msgg += "\nmaxsize:" + ndef.getMaxSize() + "bytes";
readNfcTag(intent);
}else{
msgg = "no ndef message";
}
Toast.makeText(this ,"识别到新卡",Toast.LENGTH_LONG).show();
content.setText(cardId + "\n" + msgg );
Log.d("myNFC",cardId);
Log.d("myNFC",intent.toString());
}
private void readNfcTag(Intent intent) {
if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
NfcAdapter.EXTRA_NDEF_MESSAGES);
NdefMessage msgs[] = null;
int contentSize = 0;
if (rawMsgs != null) {
msgs = new NdefMessage[rawMsgs.length];
for (int i = 0; i < rawMsgs.length; i++) {
msgs[i] = (NdefMessage) rawMsgs[i];
contentSize += msgs[i].toByteArray().length;
}
}
try {
if (msgs != null) {
NdefRecord record = msgs[0].getRecords()[0];
String textRecord = parseTextRecord(record);
msgg += "\ncontent:" + textRecord;
msgg += "\ncontentSize:" + contentSize + " bytes";
}
} catch (Exception e) {
}
}
}
代码中读取的还有一些ndef信息,但后面也都没有用上了。下面是NDEF信息的解析函数:
public static String parseTextRecord(NdefRecord ndefRecord) {
/**
* 判断数据是否为NDEF格式
*/
//判断TNF
if (ndefRecord.getTnf() != NdefRecord.TNF_WELL_KNOWN) {
return null;
}
//判断可变的长度的类型
if (!Arrays.equals(ndefRecord.getType(), NdefRecord.RTD_TEXT)) {
return null;
}
try {
//获得字节数组,然后进行分析
byte[] payload = ndefRecord.getPayload();
//下面开始NDEF文本数据第一个字节,状态字节
//判断文本是基于UTF-8还是UTF-16的,取第一个字节"位与"上16进制的80,16进制的80也就是最高位是1,
//其他位都是0,所以进行"位与"运算后就会保留最高位
String textEncoding = ((payload[0] & 0x80) == 0) ? "UTF-8" : "UTF-16";
//3f最高两位是0,第六位是1,所以进行"位与"运算后获得第六位
int languageCodeLength = payload[0] & 0x3f;
//下面开始NDEF文本数据第二个字节,语言编码
//获得语言编码
String languageCode = new String(payload, 1, languageCodeLength, "US-ASCII");
//下面开始NDEF文本数据后面的字节,解析出文本
String textRecord = new String(payload, languageCodeLength + 1,
payload.length - languageCodeLength - 1, textEncoding);
return textRecord;
} catch (Exception e) {
throw new IllegalArgumentException();
}
}
3 修改手机NFC的uid
3.1 确认原始状态
换了几台设备,读出的uid各不相同,但有一个规律:08xxxxxx。
这是源于NFCC协议的缺省,未设置情况下是以08开头的8位16进制uid。
3.2 尝试修改这个值
在代码中对找到对这个值进行设置的地方,位于:
/vendor/nxp/opensource/commonsys/external/libnfc-nci/xxxx/src/nfa/dm/nfa_dm_discover.c
通过
...
UINT8_TO_STREAM(p, NFC_PMID_LA_NFCID1)
...
可以对其进行设置。
问题:相当于将uid写死了。在后续的app中需要实现对这个uid的修改,如果在此修改则需要通过代码注入等方式,代价高。
3.3 配置文件修改
寻找配置文件,在配置文件中修改对应的值。配置文件位于:
/vendor/etc/libnfc-nxp_RF.conf
//文件位置可能不同,主要是找到NXP_CORE_CONF变量的配置位置。
对应的uid字段:NXP_CORE_CONF配置如下:
NXP_CORE_CONF={ 20, 02, 2B, 0D,
18, 01, 01,
21, 01, 00,
28, 01, 01,
30, 01, 08,
31, 01, 03,
33, 04, DC, 2C, E3, 4E,
50, 01, 02,
54, 01, 06,
5B, 01, 02,
60, 01, 0E,
80, 01, 01,
81, 01, 01,
82, 01, 0E
}
最开始用的是下面这版配置:
# Add 58, 01, 74, for changing FWI while route IsoDep type-A to HCE:
# Fix bug AlmId 26307, Jingdong pay is failure on Lakala T1 POS.
# Default TB1 of HCE is 0x44. Change TB1 to 0x74,
# then change FWT from 4.8ms to 38.7ms.
# TB1: high nibble 4bits is FWI, low nibble 4bits is SFGI.
NXP_CORE_CONF={ 20, 02, 33, 11,
18, 01, 01,
21, 01, 00,
28, 01, 00,
30, 01, 08,
31, 01, 03,
32, 01, 20,
33, 00,
38, 01, 01,
50, 01, 02,
54, 01, 06,
58, 01, 74,
5B, 01, 00,
80, 01, 01,
81, 01, 01,
82, 01, 0E,
68, 01, 01,
85, 01, 01
}
但是会报错:
10-08 09:35:43.713 916 7462 D NxpHal : phNxpNciHal_print_res_status: response status =STATUS_SYNTAX_ERROR
后面就修改掉了
其中33 , 04后跟的就是想要设置的uid。
4 总结
到此,跟NFC相关的技术内容其实就已经结束了,想要实现手机NFC模拟门禁卡,其实就是修改uid的配置。这种方式只针对最简单的非加密门禁卡。
但是当时做的时候,困难才刚刚开始。
问题1:无论给什么权限的app,都不能修改/vendor/etc/libnfc-nxp_RF.conf
看吧,这个问题真的很蠢,稍微了解一点android就会知道,vendor下的文件怎么可能给你乱搞。论Read-only-file system的含金量。当时又搞avc,又搞挂载,反正一顿操作hhh。
还出了一个乌龙:突然有一段时间能用了。猜猜是什么原因?
答案是想往手机里push文件,就需要root remount,这下好了,不是只读的了,当然就可以修改了,当时还以为解决了,白高兴半天。
但这也是为什么市面上第三方的NFC模拟卡软件都需要手机的root权限的原因,可能内部大多都是修改uid的这种逻辑。
如果不是源码开发,将手机root后,修改这个文件也可以实现NFC模拟卡功能。
因为是源码开发,后面修改:
vendor/nxp/opensource/halimpl/xxx/halimpl/hal/phNxpNciHal.cc
int phNxpNciHal_MinOpen (){
...
setNxpRfConfigPath("/xxx/xxx/xxx/xxx/libnfc-nxp_RF.conf");
...
//以及
vendor/nxp/opensource/halimpl/xxx/halimpl/utils/phNxpConfig.cc
...
char nxp_rf_config_path[256] =
"/xxx/xxx/xxx/xxx/libnfc-nxp_RF.conf";
...
将系统读config的地方换到了可修改的位置,实现对uid的修改。
问题2:avc权限问题
这也是android源码开发比较常见的问题了,解决方案也很简单。
- Step 1:抓取avc权限问题log
adb logcat | grep "avc" > avc.txt
- Step 2:使用工具audit2allow,执行audit2allow –i avc
audit2allow –i avc
- Step 3:可以输出对应需要的权限如下(例)
#============= platform_app ============== allow platform_app nfc_service:service_manager find;
- Step 4:找到对应.te文件
find -name platform_app.te
- Step 5:例中位于
./sepolicy/public/platform_app.te
- Step 6:在其中加入上面的输出
allow platform_app nfc_service:service_manager find;
重新编译就可以解决avc权限问题。
整体流程
今天的文章安卓nfc模拟软件_模拟nfc软件「建议收藏」分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/86589.html