From 0d011b876ecf05031a7daa54ab7e6d3506728a47 Mon Sep 17 00:00:00 2001 From: Linus <23507341+Linus789@users.noreply.github.com> Date: Mon, 26 Feb 2024 03:50:59 +0000 Subject: [PATCH] feat(OpeningHours): Add `Fix crash` patch (#2697) Co-authored-by: oSumAtrIX --- api/revanced-patches.api | 6 + .../misc/fix/crash/FixCrashPatch.kt | 115 ++++++++++++++++++ .../crash/fingerprints/SetPlaceFingerprint.kt | 12 ++ 3 files changed, 133 insertions(+) create mode 100644 src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt create mode 100644 src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/fingerprints/SetPlaceFingerprint.kt diff --git a/api/revanced-patches.api b/api/revanced-patches.api index eb4ddd353..e380e87b2 100644 --- a/api/revanced-patches.api +++ b/api/revanced-patches.api @@ -410,6 +410,12 @@ public final class app/revanced/patches/nyx/misc/pro/UnlockProPatch : app/revanc public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V } +public final class app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch : app/revanced/patcher/patch/BytecodePatch { + public static final field INSTANCE Lapp/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch; + public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V + public synthetic fun execute (Lapp/revanced/patcher/data/Context;)V +} + public final class app/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch : app/revanced/patcher/patch/BytecodePatch { public static final field INSTANCE Lapp/revanced/patches/photomath/detection/deviceid/SpoofDeviceIdPatch; public fun execute (Lapp/revanced/patcher/data/BytecodeContext;)V diff --git a/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt b/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt new file mode 100644 index 000000000..8e2777a74 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/FixCrashPatch.kt @@ -0,0 +1,115 @@ +package app.revanced.patches.openinghours.misc.fix.crash + +import app.revanced.patcher.data.BytecodeContext +import app.revanced.patcher.extensions.InstructionExtensions.getInstructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.extensions.newLabel +import app.revanced.patcher.patch.BytecodePatch +import app.revanced.patcher.patch.annotation.CompatiblePackage +import app.revanced.patcher.patch.annotation.Patch +import app.revanced.patches.openinghours.misc.fix.crash.fingerprints.SetPlaceFingerprint +import app.revanced.util.exception +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21t +import com.android.tools.smali.dexlib2.iface.instruction.FiveRegisterInstruction +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +@Patch( + name = "Fix crash", + compatiblePackages = [CompatiblePackage("de.simon.openinghours", ["1.0"])], +) +@Suppress("unused") +object FixCrashPatch : BytecodePatch( + setOf(SetPlaceFingerprint), +) { + + override fun execute(context: BytecodeContext) { + SetPlaceFingerprint.result?.let { + val indexedInstructions = it.mutableMethod.getInstructions().withIndex().toList() + + /** + * This function replaces all `checkNotNull` instructions in the integer interval + * from [startIndex] to [endIndex], both inclusive. In place of the `checkNotNull` + * instruction an if-null check is inserted. If the if-null check yields that + * the value is indeed null, we jump to a newly created label at `endIndex + 1`. + */ + fun avoidNullPointerException(startIndex: Int, endIndex: Int) { + val continueLabel = it.mutableMethod.newLabel(endIndex + 1) + + for (index in startIndex..endIndex) { + val instruction = indexedInstructions[index].value + + if (!instruction.isCheckNotNullInstruction) { + continue + } + + val checkNotNullInstruction = instruction as FiveRegisterInstruction + val originalRegister = checkNotNullInstruction.registerC + + it.mutableMethod.replaceInstruction( + index, + BuilderInstruction21t( + Opcode.IF_EQZ, + originalRegister, + continueLabel, + ), + ) + } + } + + val getOpeningHoursIndex = getIndicesOfInvoke( + indexedInstructions, + "Lde/simon/openinghours/models/Place;", + "getOpeningHours", + ) + + val setWeekDayTextIndex = getIndexOfInvoke( + indexedInstructions, + "Lde/simon/openinghours/views/custom/PlaceCard;", + "setWeekDayText", + ) + + val startCalculateStatusIndex = getIndexOfInvoke( + indexedInstructions, + "Lde/simon/openinghours/views/custom/PlaceCard;", + "startCalculateStatus", + ) + + // Replace the Intrinsics;->checkNotNull instructions with a null check + // and jump to our newly created label if it returns true. + // This avoids the NullPointerExceptions. + avoidNullPointerException(getOpeningHoursIndex[1], startCalculateStatusIndex) + avoidNullPointerException(getOpeningHoursIndex[0], setWeekDayTextIndex) + } ?: throw SetPlaceFingerprint.exception + } + + private fun isInvokeInstruction(instruction: Instruction, className: String, methodName: String): Boolean { + val methodRef = instruction.getReference() ?: return false + return methodRef.definingClass == className && methodRef.name == methodName + } + + private fun getIndicesOfInvoke( + instructions: List>, + className: String, + methodName: String, + ): List = instructions.mapNotNull { (index, instruction) -> + if (isInvokeInstruction(instruction, className, methodName)) { + index + } else { + null + } + } + + private fun getIndexOfInvoke( + instructions: List>, + className: String, + methodName: String, + ): Int = instructions.first { (_, instruction) -> + isInvokeInstruction(instruction, className, methodName) + }.index + + private val Instruction.isCheckNotNullInstruction + get() = isInvokeInstruction(this, "Lkotlin/jvm/internal/Intrinsics;", "checkNotNull") +} diff --git a/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/fingerprints/SetPlaceFingerprint.kt b/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/fingerprints/SetPlaceFingerprint.kt new file mode 100644 index 000000000..738ecd188 --- /dev/null +++ b/src/main/kotlin/app/revanced/patches/openinghours/misc/fix/crash/fingerprints/SetPlaceFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.openinghours.misc.fix.crash.fingerprints + +import app.revanced.patcher.fingerprint.MethodFingerprint + +internal object SetPlaceFingerprint : MethodFingerprint( + "V", + parameters = listOf("Lde/simon/openinghours/models/Place;"), + customFingerprint = { methodDef, _ -> + methodDef.definingClass == "Lde/simon/openinghours/views/custom/PlaceCard;" && + methodDef.name == "setPlace" + }, +)