From c4d8bae2bb28a87158933d92bce754b66c9b0228 Mon Sep 17 00:00:00 2001 From: VictorModi Date: Tue, 3 Feb 2026 08:06:20 +0800 Subject: [PATCH] refactor: improve NFC hooks and broadcast handling - Replace Logger with centralized `log` in NfcServiceHookBroadcastReceiver - Add PackageManagerHook to intercept `hasSystemFeature` for NFC-related features - Fix DialogHelper random UID length validation (max 65535) - Refactor BaseHook logging to use hook's own name - Simplify init methods in NfcServiceHook and NfcDispatchManagerHook - Ensure safe retrieval of NFC Service Handler and prevent null crashes - Limit NdefMessage max length to `MAX_OF_BROADCAST_SIZE` instead of Int.MAX_VALUE - Update MainHook to initialize multiple hooks safely with improved logging --- app/src/main/java/mba/vm/onhit/Constant.kt | 6 +++ .../java/mba/vm/onhit/helper/DialogHelper.kt | 2 +- .../main/java/mba/vm/onhit/hook/BaseHook.kt | 4 +- .../main/java/mba/vm/onhit/hook/MainHook.kt | 17 ++++---- .../vm/onhit/hook/NfcDispatchManagerHook.kt | 8 +--- .../java/mba/vm/onhit/hook/NfcServiceHook.kt | 40 ++++++++++--------- .../mba/vm/onhit/hook/PackageManagerHook.kt | 24 +++++++++++ .../NfcServiceHookBroadcastReceiver.kt | 4 +- 8 files changed, 69 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/mba/vm/onhit/hook/PackageManagerHook.kt diff --git a/app/src/main/java/mba/vm/onhit/Constant.kt b/app/src/main/java/mba/vm/onhit/Constant.kt index a289e0f..41da603 100644 --- a/app/src/main/java/mba/vm/onhit/Constant.kt +++ b/app/src/main/java/mba/vm/onhit/Constant.kt @@ -1,8 +1,12 @@ package mba.vm.onhit +import android.content.pm.PackageManager + class Constant { companion object { const val NFC_SERVICE_PACKAGE_NAME = "com.android.nfc" + // Hide on Android API + const val PACKAGE_MANAGER_FEATURE_NFC_ANY = "android.hardware.nfc.any" const val BROADCAST_TAG_EMULATOR_REQUEST = "${BuildConfig.APPLICATION_ID}.TAG_EMULATOR_REQUEST" const val SHARED_PREFERENCES_NAME = BuildConfig.APPLICATION_ID const val SHARED_PREFERENCES_CHOSEN_FOLDER = "chosen_folder" @@ -11,5 +15,7 @@ class Constant { const val PREF_RANDOM_UID_LEN = "pref_random_uid_len" const val MAX_OF_BROADCAST_SIZE = 1048576 const val GITHUB_URL = "https://github.com/0penPublic/onHit" + + val PACKAGE_MANAGER_SYSTEM_NFC_FEATURES = setOf(PackageManager.FEATURE_NFC, PACKAGE_MANAGER_FEATURE_NFC_ANY) } } \ No newline at end of file diff --git a/app/src/main/java/mba/vm/onhit/helper/DialogHelper.kt b/app/src/main/java/mba/vm/onhit/helper/DialogHelper.kt index f9a0ff2..d4266c8 100644 --- a/app/src/main/java/mba/vm/onhit/helper/DialogHelper.kt +++ b/app/src/main/java/mba/vm/onhit/helper/DialogHelper.kt @@ -155,7 +155,7 @@ object DialogHelper { ConfigManager.setFixedUidValue(context, hex) } else { val len = input.toIntOrNull() - if (len != null && len in 0..65536) { + if (len != null && len in 0..65535) { setNormalTextColor(etUidConfig, context) ConfigManager.setRandomUidLen(context, input) } else { diff --git a/app/src/main/java/mba/vm/onhit/hook/BaseHook.kt b/app/src/main/java/mba/vm/onhit/hook/BaseHook.kt index d55652c..aa63071 100644 --- a/app/src/main/java/mba/vm/onhit/hook/BaseHook.kt +++ b/app/src/main/java/mba/vm/onhit/hook/BaseHook.kt @@ -6,7 +6,7 @@ import mba.vm.onhit.BuildConfig abstract class BaseHook { abstract val name: String - abstract fun init(classLoader: ClassLoader?) + abstract fun init(classLoader: ClassLoader) - fun log(text: String) = if (BuildConfig.DEBUG) Logger.i("[ onHit ] [ ${NfcServiceHook.name} ] $text") else Unit + fun log(text: String) = if (BuildConfig.DEBUG) Logger.i("[ onHit ] [ $name ] $text") else Unit } \ No newline at end of file diff --git a/app/src/main/java/mba/vm/onhit/hook/MainHook.kt b/app/src/main/java/mba/vm/onhit/hook/MainHook.kt index 57c795e..992f474 100644 --- a/app/src/main/java/mba/vm/onhit/hook/MainHook.kt +++ b/app/src/main/java/mba/vm/onhit/hook/MainHook.kt @@ -4,6 +4,7 @@ import de.robv.android.xposed.IXposedHookLoadPackage import de.robv.android.xposed.XposedBridge import de.robv.android.xposed.callbacks.XC_LoadPackage import io.github.kyuubiran.ezxhelper.xposed.EzXposed +import mba.vm.onhit.BuildConfig import mba.vm.onhit.Constant.Companion.NFC_SERVICE_PACKAGE_NAME class MainHook : IXposedHookLoadPackage { @@ -12,17 +13,19 @@ class MainHook : IXposedHookLoadPackage { EzXposed.initHandleLoadPackage(lpparam) when (lpparam.packageName) { NFC_SERVICE_PACKAGE_NAME -> { - initHook(NfcServiceHook, lpparam.classLoader) - initHook(NfcDispatchManagerHook, lpparam.classLoader) + initHook(lpparam.classLoader, NfcServiceHook, NfcDispatchManagerHook, PackageManagerHook) } + else -> initHook(lpparam.classLoader, PackageManagerHook) } } - private fun initHook(hook: BaseHook, classLoader: ClassLoader?) { - try { - hook.init(classLoader) - } catch (e: Exception) { - XposedBridge.log(e) + private fun initHook(classLoader: ClassLoader, vararg hooks: BaseHook) { + hooks.forEach { hook -> + try { + hook.init(classLoader) + } catch (e: Exception) { + XposedBridge.log("[ ${BuildConfig.APPLICATION_ID} ] Failed to Init ${hook.name}, ${e.message}") + } } } } \ No newline at end of file diff --git a/app/src/main/java/mba/vm/onhit/hook/NfcDispatchManagerHook.kt b/app/src/main/java/mba/vm/onhit/hook/NfcDispatchManagerHook.kt index 7801e66..c04e9b9 100644 --- a/app/src/main/java/mba/vm/onhit/hook/NfcDispatchManagerHook.kt +++ b/app/src/main/java/mba/vm/onhit/hook/NfcDispatchManagerHook.kt @@ -12,13 +12,9 @@ import mba.vm.onhit.BuildConfig * when this app is in the foreground. */ object NfcDispatchManagerHook : BaseHook() { - override val name: String = this::class.simpleName!! + override val name: String = this.javaClass.simpleName - override fun init(classLoader: ClassLoader?) { - classLoader ?: run { - log("nfcClassLoader is null") - return - } + override fun init(classLoader: ClassLoader) { val clazz = try { Class.forName("com.oplus.nfc.dispatch.NfcDispatchManager", false, classLoader) } catch (_: ClassNotFoundException) { diff --git a/app/src/main/java/mba/vm/onhit/hook/NfcServiceHook.kt b/app/src/main/java/mba/vm/onhit/hook/NfcServiceHook.kt index 1ec1a2b..bc77fd4 100644 --- a/app/src/main/java/mba/vm/onhit/hook/NfcServiceHook.kt +++ b/app/src/main/java/mba/vm/onhit/hook/NfcServiceHook.kt @@ -4,6 +4,7 @@ import android.app.Application import android.content.IntentFilter import android.nfc.NdefMessage import android.os.Bundle +import android.os.Handler import androidx.core.content.ContextCompat import de.robv.android.xposed.XposedHelpers.findClass import io.github.kyuubiran.ezxhelper.core.finder.MethodFinder @@ -11,6 +12,7 @@ import io.github.kyuubiran.ezxhelper.core.helper.ObjectHelper.`-Static`.objectHe import io.github.kyuubiran.ezxhelper.xposed.dsl.HookFactory.`-Static`.createHook import mba.vm.onhit.BuildConfig import mba.vm.onhit.Constant +import mba.vm.onhit.Constant.Companion.MAX_OF_BROADCAST_SIZE import mba.vm.onhit.core.TagTechnology import mba.vm.onhit.hook.boardcast.NfcServiceHookBroadcastReceiver import java.lang.reflect.Method @@ -18,21 +20,16 @@ import java.lang.reflect.Proxy object NfcServiceHook : BaseHook() { - private lateinit var nfcServiceHandler: Any private lateinit var nfcService: Any + private lateinit var nfcServiceHandler: Handler private lateinit var nfcClassLoader: ClassLoader private lateinit var dispatchTagEndpoint: Method private lateinit var tagEndpointInterface: Class<*> - override val name: String = this::class.simpleName!! + override val name: String = this.javaClass.simpleName - override fun init(classLoader: ClassLoader?) { - classLoader?.let { - nfcClassLoader = classLoader - } ?: run { - log("nfcClassLoader is null") - return - } + override fun init(classLoader: ClassLoader) { + nfcClassLoader = classLoader tagEndpointInterface = findClass($$"com.android.nfc.DeviceHost$TagEndpoint", nfcClassLoader) MethodFinder.fromClass("com.android.nfc.NfcApplication", nfcClassLoader) .filterByName("onCreate") @@ -42,10 +39,14 @@ object NfcServiceHook : BaseHook() { val app = params.thisObject as? Application app?.let { nfcService = app.objectHelper().getObjectOrNull("mNfcService") ?: run { - log("Cannot get NFC Service now") + log("Cannot get NFC Service now, Hook Failed. Is NFC Service Working?") + return@after + } + nfcServiceHandler = nfcService.objectHelper().getObjectOrNull("mHandler") as? Handler?: run { + log("Cannot get NFC Service Handler, Hook Failed.") + return@after } - nfcServiceHandler = nfcService.objectHelper().getObjectOrNull("mHandler")!! - dispatchTagEndpoint = MethodFinder.fromClass(nfcServiceHandler::class) + dispatchTagEndpoint = MethodFinder.fromClass(nfcServiceHandler.javaClass) .filterByName("dispatchTagEndpoint") .first() if (BuildConfig.DEBUG) nfcService.objectHelper().setObject("DBG", true) @@ -67,13 +68,16 @@ object NfcServiceHook : BaseHook() { ndef: NdefMessage? ) { val tag = buildFakeTag(uid, ndef) - dispatchTagEndpoint.invoke( - nfcServiceHandler, - tag, nfcService.objectHelper().getObjectOrNull("mReaderModeParams")) ?: run { - log("mReaderModeParams is null") + nfcServiceHandler.post { + dispatchTagEndpoint.invoke( + nfcServiceHandler, + tag, + nfcService.objectHelper().getObjectOrNull("mReaderModeParams") + ) } } + private fun buildFakeTag( uid: ByteArray, ndef: NdefMessage?, @@ -94,9 +98,9 @@ object NfcServiceHook : BaseHook() { "getTechExtras" -> { val ndefBundle = Bundle().apply { putParcelable("ndefmsg", ndef) - putInt("ndefmaxlength", Int.MAX_VALUE) + putInt("ndefmaxlength", MAX_OF_BROADCAST_SIZE) putInt("ndefcardstate", 1) - putInt("ndeftype", 4) + putInt("ndeftype", 2) } arrayOf(ndefBundle) } diff --git a/app/src/main/java/mba/vm/onhit/hook/PackageManagerHook.kt b/app/src/main/java/mba/vm/onhit/hook/PackageManagerHook.kt new file mode 100644 index 0000000..61d3be4 --- /dev/null +++ b/app/src/main/java/mba/vm/onhit/hook/PackageManagerHook.kt @@ -0,0 +1,24 @@ +package mba.vm.onhit.hook + +import io.github.kyuubiran.ezxhelper.core.finder.MethodFinder +import io.github.kyuubiran.ezxhelper.xposed.dsl.HookFactory.`-Static`.createHook +import mba.vm.onhit.Constant.Companion.PACKAGE_MANAGER_SYSTEM_NFC_FEATURES + +object PackageManagerHook : BaseHook() { + override val name: String = this.javaClass.simpleName + override fun init(classLoader: ClassLoader) { + MethodFinder.fromClass( + "android.app.ApplicationPackageManager", + classLoader + ) + .filterByName("hasSystemFeature") + .first() + .createHook { + before { param -> + log("hasSystemFeature called in ${param.thisObject.javaClass.name}") + if (param.args.isEmpty()) return@before + if (param.args[0] as? String in PACKAGE_MANAGER_SYSTEM_NFC_FEATURES) param.result = false + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/mba/vm/onhit/hook/boardcast/NfcServiceHookBroadcastReceiver.kt b/app/src/main/java/mba/vm/onhit/hook/boardcast/NfcServiceHookBroadcastReceiver.kt index a04a0f9..5badcae 100644 --- a/app/src/main/java/mba/vm/onhit/hook/boardcast/NfcServiceHookBroadcastReceiver.kt +++ b/app/src/main/java/mba/vm/onhit/hook/boardcast/NfcServiceHookBroadcastReceiver.kt @@ -5,13 +5,13 @@ import android.content.Context import android.content.Intent import android.nfc.NdefMessage import androidx.core.content.IntentCompat -import io.github.kyuubiran.ezxhelper.android.logging.Logger import mba.vm.onhit.Constant +import mba.vm.onhit.hook.NfcDispatchManagerHook.log import mba.vm.onhit.hook.NfcServiceHook class NfcServiceHookBroadcastReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - Logger.i("${this.javaClass.name} onReceive: ${intent.action}") + log("${this.javaClass.name} onReceive: ${intent.action}") when (intent.action) { Constant.BROADCAST_TAG_EMULATOR_REQUEST -> { val uid = intent.getByteArrayExtra("uid")