diff --git a/patches/api/patches.api b/patches/api/patches.api index 7d32bec85..382490791 100644 --- a/patches/api/patches.api +++ b/patches/api/patches.api @@ -244,6 +244,75 @@ public final class app/revanced/patches/irplus/ad/RemoveAdsPatchKt { public static final fun getRemoveAdsPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } +public final class app/revanced/patches/kakaotalk/ads/RemoveBizBoardPatchKt { + public static final fun getRemoveBizBoardPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/ads/RemoveFocusAdPatchKt { + public static final fun getRemoveFocusAdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/ads/RemoveMoreTabAdPatchKt { + public static final fun getRemoveMoreTabAdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/ads/RemoveNativeAdPatchKt { + public static final fun getRemoveNativeAdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/ads/RemoveOlkChatRoomListAdPatchKt { + public static final fun getRemoveOlkChatRoomListAdPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/changemodel/ChangeModelPatchKt { + public static final fun getChangeModelPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/chatlog/Remove99ClampPatchKt { + public static final fun getRemove99ClampPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/chatroom/Remove300PlusLimitPatchKt { + public static final fun getRemove300PlusLimitBaseChatRoomPatch ()Lapp/revanced/patcher/patch/BytecodePatch; + public static final fun getRemove300PlusLimitOpenChatRoomPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/emoticon/ForceEnableEmoticonPlusPatchKt { + public static final fun getForceEnableEmoticonPlusPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/ghost/GhostModePatchKt { + public static final fun getGhostMode ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/integrity/BypassMoatCheckPatchKt { + public static final fun getBypassMoatCheckPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/integrity/BypassRequestChecksumsPatchKt { + public static final fun getBypassRequestChecksumPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/integrity/VerifyingSignaturePatchKt { + public static final fun getVerifyingSignaturePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/misc/ForceEnableDebugModePatchKt { + public static final fun getForceEnableDebugModePatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/misc/RemoveShopTabPatchKt { + public static final fun getRemoveShopTabPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/send/AllowReplyToFeedPatchKt { + public static final fun getAllowReplyToFeedPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + +public final class app/revanced/patches/kakaotalk/versioninfo/VersionInfoPatchKt { + public static final fun getVersionInfoPatch ()Lapp/revanced/patcher/patch/BytecodePatch; +} + public final class app/revanced/patches/lightroom/misc/login/DisableMandatoryLoginPatchKt { public static final fun getDisableMandatoryLoginPatch ()Lapp/revanced/patcher/patch/BytecodePatch; } diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveBizBoardPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveBizBoardPatch.kt new file mode 100644 index 000000000..4274d9a41 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveBizBoardPatch.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.kakaotalk.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.ads.fingerprints.measuringBizBoardFingerprint + +@Suppress("unused") +val removeBizBoardPatch = bytecodePatch( + name = "Remove BizBoard ads", + description = "Removes the BizBoard ad by forcing its dimensions to 0x0 and visibility to GONE in onMeasure.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = measuringBizBoardFingerprint.method + + method.addInstructions( + 0, + """ + const/4 v0, 0x0 + invoke-virtual {p0, v0, v0}, Landroid/view/View;->setMeasuredDimension(II)V + + const/16 v0, 0x8 + invoke-virtual {p0, v0}, Landroid/view/View;->setVisibility(I)V + + return-void + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveFocusAdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveFocusAdPatch.kt new file mode 100644 index 000000000..e92aa892d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveFocusAdPatch.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.kakaotalk.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.ads.fingerprints.loadFocusAdFingerprint + +val removeFocusAdPatch = bytecodePatch( + name = "Remove focus ad", + description = "Removes the focus ad from the app.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + loadFocusAdFingerprint.method.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveMoreTabAdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveMoreTabAdPatch.kt new file mode 100644 index 000000000..fd03e92fd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveMoreTabAdPatch.kt @@ -0,0 +1,45 @@ +package app.revanced.patches.kakaotalk.ads + +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.ads.fingerprints.adBigUIModelFingerprint +import app.revanced.patches.kakaotalk.ads.fingerprints.addSectionToMoreTabUIFingerprint +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +val removeMoreTabAdPatch = bytecodePatch( + name = "Remove More tab ad", + description = "Removes the ad from the More tab.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val addSectionToMoreTabUIMethod = addSectionToMoreTabUIFingerprint.method + val addSectionToMoreTabUIInsns = addSectionToMoreTabUIMethod.instructions + + val adBigUIModelClass = adBigUIModelFingerprint.method.definingClass + + val matches = addSectionToMoreTabUIInsns.mapIndexedNotNull { idx, inst -> + if (inst is BuilderInstruction35c + && inst.opcode == Opcode.INVOKE_VIRTUAL + && (inst.getReference()?.name == "add") + ) { + val prev = addSectionToMoreTabUIInsns.getOrNull(idx - 1) as? BuilderInstruction35c + val ref = (prev?.getReference()) + if (ref?.definingClass == adBigUIModelClass) { + Pair(idx - 1, idx) + } else null + } else null + } + + matches + .sortedByDescending { it.second } + .forEach { (loadIdx, invokeIdx) -> + addSectionToMoreTabUIMethod.removeInstruction(invokeIdx) + addSectionToMoreTabUIMethod.removeInstruction(loadIdx) + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveNativeAdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveNativeAdPatch.kt new file mode 100644 index 000000000..34147c0f9 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveNativeAdPatch.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.kakaotalk.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.ads.fingerprints.loadNativeAdFingerprint + +val removeNativeAdPatch = bytecodePatch( + name = "Remove native ad", + description = "Removes the native ad from the app.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = loadNativeAdFingerprint.method + + method.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveOlkChatRoomListAdPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveOlkChatRoomListAdPatch.kt new file mode 100644 index 000000000..edcd937a0 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/RemoveOlkChatRoomListAdPatch.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.kakaotalk.ads + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.ads.fingerprints.addOlkChatRoomListAdFingerprint +import app.revanced.patches.kakaotalk.common.fingerprints.kotlinUnitInstanceFingerprint + +@Suppress("unused") +val removeOlkChatRoomListAdPatch = bytecodePatch( + name = "Remove OpenLink chat room list ad", + description = "Removes the OpenLink chat room list ad.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val findUnit = kotlinUnitInstanceFingerprint.method + val unitClass = findUnit.definingClass + + val method = addOlkChatRoomListAdFingerprint.method + + // I tried to find the field name, but it's pretty obvious to me, so I hardcode it. + // If it changes, we need to fix it + method.addInstructions( + 0, + """ + sget-object v0, $unitClass->a:$unitClass + return-object v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveBizBoardFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveBizBoardFingerprint.kt new file mode 100644 index 000000000..3fb5ba077 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveBizBoardFingerprint.kt @@ -0,0 +1,27 @@ +package app.revanced.patches.kakaotalk.ads.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val measuringBizBoardFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + returns("V") + parameters("I", "I") + opcodes( + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.INT_TO_FLOAT, + Opcode.CONST, + Opcode.MUL_FLOAT_2ADDR, + Opcode.FLOAT_TO_INT, + Opcode.IGET, + Opcode.IF_LE, + Opcode.MOVE, + Opcode.CONST_HIGH16, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.INVOKE_SUPER, + Opcode.RETURN_VOID + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveFocusAdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveFocusAdFingerprint.kt new file mode 100644 index 000000000..2187519c6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveFocusAdFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.kakaotalk.ads.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val loadFocusAdFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC) + parameters("Lcom/kakao/adfit/ads/focus/FocusAdLoader\$OnAdLoadListener;") + returns("Z") + strings("listener", " owner is destroyed.", " loading is already started.", "Request Focus AD", " loading is started.", "Focus ad is cached. [id = ") + opcodes( + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.CONST_4, + Opcode.IF_NE, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveMoreTabAdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveMoreTabAdFingerprint.kt new file mode 100644 index 000000000..0a1eb6012 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveMoreTabAdFingerprint.kt @@ -0,0 +1,46 @@ +package app.revanced.patches.kakaotalk.ads.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val addSectionToMoreTabUIFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Ljava/lang/Object;") + returns("Ljava/lang/Object;") + strings( + "call to \'resume\' before \'invoke\' with coroutine", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.KakaoPayUiModel", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.WalletUiModel", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.WeatherUiModel", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.KakaoNowUiModel", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.TalkManualUiModel", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.ServiceGroupUiModel", + "null cannot be cast to non-null type com.kakao.talk.moretab.ui.model.WalletBannerUiModel", + "null cannot be cast to non-null type kotlin.Boolean", + ) + opcodes( + Opcode.SGET_OBJECT, + Opcode.IGET, + Opcode.CONST_4, + Opcode.IF_EQZ, + ) +} + +internal val adBigUIModelFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + returns("Ljava/lang/String;") + strings( + "AdBig(uiModel=", + ) + opcodes( + Opcode.NEW_INSTANCE, + Opcode.CONST_STRING, + Opcode.INVOKE_DIRECT, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.CONST_STRING, + ) + custom { method, classDef -> method.name == "toString" } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveNativeAdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveNativeAdFingerprint.kt new file mode 100644 index 000000000..384fd0d10 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveNativeAdFingerprint.kt @@ -0,0 +1,34 @@ +package app.revanced.patches.kakaotalk.ads.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val loadNativeAdFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("I", "Lcom/kakao/adfit/ads/media/NativeAdLoader\$AdLoadListener;") + returns("Z") + strings( + "listener", + " owner is destroyed.", + " loading is already started.", + "Request Native AD", + " loading is started.", + "Native ad is cached. [id = ", + "] [dsp = ", + "] [count = ", + "Invalid Count: " + ) + opcodes( + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.IF_LEZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.CONST_4, + Opcode.IF_NE, + ) + custom { method, classDef -> method.name == "load" } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveOlkChatRoomListAdFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveOlkChatRoomListAdFingerprint.kt new file mode 100644 index 000000000..468416b83 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ads/fingerprints/RemoveOlkChatRoomListAdFingerprint.kt @@ -0,0 +1,25 @@ +package app.revanced.patches.kakaotalk.ads.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val addOlkChatRoomListAdFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Ljava/lang/Object;") + returns("Ljava/lang/Object;") + strings("list", "key_ad_info", "") + opcodes( + Opcode.MOVE_OBJECT_FROM16, + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC_RANGE, + Opcode.SGET_BOOLEAN, + Opcode.IGET_OBJECT, + Opcode.IGET, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.CONST_STRING, + Opcode.IGET_OBJECT, + Opcode.IF_EQZ, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/changemodel/ChangeModelPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/changemodel/ChangeModelPatch.kt new file mode 100644 index 000000000..75a285eba --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/changemodel/ChangeModelPatch.kt @@ -0,0 +1,28 @@ +package app.revanced.patches.kakaotalk.changemodel + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patcher.patch.stringOption +import app.revanced.patches.kakaotalk.changemodel.fingerprints.changeModelFingerprint + +@Suppress("unused") +val changeModelPatch = bytecodePatch( + name = "Change model", + description = "Changes the device model to supporting subdevice features", +) { + val changeModelOption = stringOption( + "model", "SM-X926N" + ) + + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + changeModelFingerprint.method.addInstructions( + 0, + """ + const-string v0, "${changeModelOption.value}" + return-object v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/changemodel/fingerprints/ChangeModelFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/changemodel/fingerprints/ChangeModelFingerprint.kt new file mode 100644 index 000000000..8d59e0fad --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/changemodel/fingerprints/ChangeModelFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.kakaotalk.changemodel.fingerprints + +import app.revanced.patcher.fingerprint + +@Suppress("unused") +internal val changeModelFingerprint = fingerprint { + strings("MODEL", "\\s", "-", "US", "toUpperCase(...)") + custom { + _, classDef -> + classDef.methods.indexOf(classDef.methods.last()) > 2 + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/Remove99ClampPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/Remove99ClampPatch.kt new file mode 100644 index 000000000..594d2f9fd --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/Remove99ClampPatch.kt @@ -0,0 +1,30 @@ +package app.revanced.patches.kakaotalk.chatlog + +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.chatlog.fingerprints.processWatermarkCountFingerprint +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction10t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t + +@Suppress("unused") +val remove99ClampPatch = bytecodePatch( + name = "Disable 99 unread limit", + description = "Skip the 99-cap so unread count shows full value" +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = processWatermarkCountFingerprint.method + + method.instructions + .filterIsInstance() + .filter { it.opcode == Opcode.IF_LE } + .forEach { ifle -> + val idx = method.instructions.indexOf(ifle) + val goto = BuilderInstruction10t(Opcode.GOTO, ifle.target) + method.replaceInstruction(idx, goto) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/fingerprints/Remove99ClampFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/fingerprints/Remove99ClampFingerprint.kt new file mode 100644 index 000000000..933c356fc --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatlog/fingerprints/Remove99ClampFingerprint.kt @@ -0,0 +1,11 @@ +package app.revanced.patches.kakaotalk.chatlog.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +internal val processWatermarkCountFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Object;") + parameters() + strings("notiRead", "openlinkSettingShowUnreadCount") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatroom/Remove300PlusLimitPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatroom/Remove300PlusLimitPatch.kt new file mode 100644 index 000000000..faf73310d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatroom/Remove300PlusLimitPatch.kt @@ -0,0 +1,58 @@ +package app.revanced.patches.kakaotalk.chatroom + +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.chatroom.fingerprints.limit300PlusBaseChatRoomFingerprint +import app.revanced.patches.kakaotalk.chatroom.fingerprints.limit300PlusOpenChatRoomFingerprint +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction10t +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22t + +@Suppress("unused") +val remove300PlusLimitBaseChatRoomPatch = bytecodePatch( + name = "Disable 300+ unread limit (BaseChatRoom)", + description = "Always show the real unread count instead of '300+' in base chatroom list" +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = limit300PlusBaseChatRoomFingerprint.method + + val branches = method.instructions + .filterIsInstance() + .filter { it.opcode == Opcode.IF_LT } + .toList() + + branches.forEach { iflt -> + val idx = method.instructions.indexOf(iflt) + val gotoInsn = BuilderInstruction10t( + Opcode.GOTO, + iflt.target + ) + method.replaceInstruction(idx, gotoInsn) + } + } +} + +@Suppress("unused") +val remove300PlusLimitOpenChatRoomPatch = bytecodePatch( + name = "Disable 300+ unread limit (OpenChatRoom)", + description = "Always show the real unread count instead of '300+' in open chatroom list" +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = limit300PlusOpenChatRoomFingerprint.method + + method.instructions + .filterIsInstance() + .filter { it.opcode == Opcode.IF_LT } + .toList() + .forEach { iflt -> + val idx = method.instructions.indexOf(iflt) + val gotoInsn = BuilderInstruction10t(Opcode.GOTO, iflt.target) + method.replaceInstruction(idx, gotoInsn) + } + } +} diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatroom/fingerprints/Remove300PlusLimitFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatroom/fingerprints/Remove300PlusLimitFingerprint.kt new file mode 100644 index 000000000..7a01753c6 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/chatroom/fingerprints/Remove300PlusLimitFingerprint.kt @@ -0,0 +1,20 @@ +package app.revanced.patches.kakaotalk.chatroom.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +@Suppress("unused") +internal val limit300PlusBaseChatRoomFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters("Lcom/kakao/talk/widget/ViewBindable;") + strings("300+") +} + +@Suppress("unused") +internal val limit300PlusOpenChatRoomFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + strings("300+") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/common/fingerprints/KotlinUnitInstanceFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/common/fingerprints/KotlinUnitInstanceFingerprint.kt new file mode 100644 index 000000000..f1c381942 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/common/fingerprints/KotlinUnitInstanceFingerprint.kt @@ -0,0 +1,19 @@ +package app.revanced.patches.kakaotalk.common.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val kotlinUnitInstanceFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + returns("Ljava/lang/String;") + strings("kotlin.Unit") + opcodes( + Opcode.CONST_STRING, + Opcode.RETURN_OBJECT + ) + custom { method, classDef -> + method.name == "toString" + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/emoticon/ForceEnableEmoticonPlusPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/emoticon/ForceEnableEmoticonPlusPatch.kt new file mode 100644 index 000000000..fd19b2f07 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/emoticon/ForceEnableEmoticonPlusPatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.kakaotalk.emoticon + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.emoticon.fingerprints.isEnableEmoticonPlusFingerprint + +@Suppress("unused") +val forceEnableEmoticonPlusPatch = bytecodePatch( + name = "Force enable emoticon plus feature", + description = "Force enable emoticon plus feature (Unpurchased emoticon can be sent once per day)", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + isEnableEmoticonPlusFingerprint.method.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/emoticon/fingerprints/ForceEnableEmoticonPlusFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/emoticon/fingerprints/ForceEnableEmoticonPlusFingerprint.kt new file mode 100644 index 000000000..149f48a44 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/emoticon/fingerprints/ForceEnableEmoticonPlusFingerprint.kt @@ -0,0 +1,22 @@ +package app.revanced.patches.kakaotalk.emoticon.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val isEnableEmoticonPlusFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters() + strings("emoticonPlusMe") + opcodes( + Opcode.SGET_OBJECT, + Opcode.IF_EQZ, + Opcode.IGET_BOOLEAN, + Opcode.RETURN, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_4, + Opcode.THROW, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ghost/GhostModePatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ghost/GhostModePatch.kt new file mode 100644 index 000000000..008f65e64 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ghost/GhostModePatch.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.kakaotalk.ghost + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.common.fingerprints.kotlinUnitInstanceFingerprint +import app.revanced.patches.kakaotalk.ghost.fingerprints.sendCurrentActionFingerprint + +@Suppress("unused") +val ghostMode = bytecodePatch( + name = "Ghost Mode", + description = "Don't expose your typing status to the other party.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val findUnit = kotlinUnitInstanceFingerprint.method + val unitClass = findUnit.definingClass + + val method = sendCurrentActionFingerprint.method + + // I tried to find the field name, but it's pretty obvious to me, so I hardcode it. + // If it changes, we need to fix it + method.addInstructions( + 0, + """ + sget-object v0, $unitClass->a:$unitClass + return-object v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ghost/fingerprints/GhostModeFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ghost/fingerprints/GhostModeFingerprint.kt new file mode 100644 index 000000000..a5cbb99ac --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/ghost/fingerprints/GhostModeFingerprint.kt @@ -0,0 +1,38 @@ +package app.revanced.patches.kakaotalk.ghost.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val sendCurrentActionFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Object;") + parameters("Ljava/lang/Object;") + strings( + "method", + "chatId", + "type", + "linkId" + ) + opcodes( + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_EQZ, + Opcode.IGET_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.NEW_INSTANCE, + Opcode.IGET_WIDE, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_WIDE, + Opcode.CONST_WIDE_16, + Opcode.CMP_LONG, + Opcode.IF_GEZ, + Opcode.GOTO + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/BypassMoatCheckPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/BypassMoatCheckPatch.kt new file mode 100644 index 000000000..9d8c5fa51 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/BypassMoatCheckPatch.kt @@ -0,0 +1,79 @@ +package app.revanced.patches.kakaotalk.integrity + +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.integrity.fingerprints.moatCheckResultFingerprintOne +import app.revanced.patches.kakaotalk.integrity.fingerprints.moatCheckResultFingerprintTwo +import app.revanced.patches.kakaotalk.integrity.fingerprints.postprocessMoatCheckFailedFingerprint +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c +import com.android.tools.smali.dexlib2.iface.instruction.Instruction +import com.android.tools.smali.dexlib2.iface.reference.MethodReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableFieldReference + +@Suppress("unused") +val bypassMoatCheckPatch = bytecodePatch( + name = "Bypass Moat check", + description = "Bypass Moat check that prevents the app from running.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val patch: (Fingerprint) -> Unit = { + val method = it.method + val insns = method.instructions + + insns + .filterIsInstance() + .filter { inst -> + inst.opcode == Opcode.SGET_OBJECT && + inst.reference == ImmutableFieldReference( + "Ljava/lang/Boolean;", "FALSE", "Ljava/lang/Boolean;" + ) + } + .forEach { inst -> + val idx = insns.indexOf(inst) + method.replaceInstruction( + idx, + BuilderInstruction21c( + Opcode.SGET_OBJECT, + inst.registerA, + ImmutableFieldReference( + "Ljava/lang/Boolean;", "TRUE", "Ljava/lang/Boolean;" + ) + ) + ) + } + + val postprocessMoatCheckFailedMethod = postprocessMoatCheckFailedFingerprint.method + + val toRemove = mutableListOf() + insns.forEachIndexed { i, inst -> + if (inst is BuilderInstruction35c && + inst.opcode == Opcode.INVOKE_VIRTUAL && + (inst.getReference()?.name == postprocessMoatCheckFailedMethod.name) && + inst.getReference()?.definingClass == + "Lcom/kakaopay/shared/security/moat/PaySecurityWorker;" + ) { + for (j in 0..3) { + insns.getOrNull(i + j)?.let { toRemove += it } + } + } + } + toRemove + .distinct() + .sortedByDescending { insns.indexOf(it) } + .forEach { inst -> + method.removeInstruction(insns.indexOf(inst)) + } + } + + patch(moatCheckResultFingerprintOne) + patch(moatCheckResultFingerprintTwo) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/BypassRequestChecksumsPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/BypassRequestChecksumsPatch.kt new file mode 100644 index 000000000..76baf286c --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/BypassRequestChecksumsPatch.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.kakaotalk.integrity + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.common.fingerprints.kotlinUnitInstanceFingerprint +import app.revanced.patches.kakaotalk.integrity.fingerprints.requestChecksumsFingerprint + +@Suppress("unused") +val bypassRequestChecksumPatch = bytecodePatch( + name = "Bypass requestChecksums", + description = "Prevents the execution of checksum verification logic by making it return early." +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val findUnit = kotlinUnitInstanceFingerprint.method + val unitClass = findUnit.definingClass + + val method = requestChecksumsFingerprint.method + + // I tried to find the field name, but it's pretty obvious to me, so I hardcode it. + // If it changes, we need to fix it + method.addInstructions( + 0, + """ + sget-object v0, $unitClass->a:$unitClass + return-object v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/VerifyingSignaturePatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/VerifyingSignaturePatch.kt new file mode 100644 index 000000000..21527ddba --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/VerifyingSignaturePatch.kt @@ -0,0 +1,23 @@ +package app.revanced.patches.kakaotalk.integrity + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.integrity.fingerprints.verifyingSignatureFingerprint + +@Suppress("unused") +val verifyingSignaturePatch = bytecodePatch( + name = "Disable verifying signature", + description = "Disables the signature verification check that prevents the app from running.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + verifyingSignatureFingerprint.method.addInstructions( + 0, + """ + const/4 v0, 0x1 + return v0 + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/BypassMoatCheckFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/BypassMoatCheckFingerprint.kt new file mode 100644 index 000000000..bb88bd449 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/BypassMoatCheckFingerprint.kt @@ -0,0 +1,65 @@ +package app.revanced.patches.kakaotalk.integrity.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val moatCheckResultFingerprintOne = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Ljava/lang/Object;", "Ljava/lang/Object;", "Ljava/lang/Object;") + returns("Ljava/lang/Object;") + strings("detectResult", "", "") + opcodes( + Opcode.CHECK_CAST, + Opcode.CHECK_CAST, + Opcode.CHECK_CAST, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.SGET_OBJECT, + Opcode.SGET_OBJECT, + Opcode.CONST_4, + Opcode.IF_NE, + Opcode.CHECK_CAST, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT, + ) +} + +internal val moatCheckResultFingerprintTwo = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Ljava/lang/Object;", "Ljava/lang/Object;", "Ljava/lang/Object;") + returns("Ljava/lang/Object;") + strings("detectResult", "", "") + opcodes( + Opcode.CHECK_CAST, + Opcode.CHECK_CAST, + Opcode.CHECK_CAST, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CHECK_CAST, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT, + Opcode.SGET_OBJECT, + Opcode.IF_EQZ, + ) +} + +internal val postprocessMoatCheckFailedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Lcom/kakaopay/kpsd/moat/sdk/MoatFlag;", "Ljava/lang/String;", "[Ljava/lang/String;") + strings("msg_title", "msg_body", "OUTPUT_KEY_FAILURE_TITLE", "OUTPUT_KEY_FAILURE_REASON", "ADS_BLOCK은 result message를 사용해야 합니다.", "let(...)", "OUTPUT_KEY_FAILURE_TYPE", "OUTPUT_KEY_PACKAGE_NAMES") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/BypassRequestChecksumsFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/BypassRequestChecksumsFingerprint.kt new file mode 100644 index 000000000..63cba53da --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/BypassRequestChecksumsFingerprint.kt @@ -0,0 +1,36 @@ +package app.revanced.patches.kakaotalk.integrity.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val requestChecksumsFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("Ljava/lang/Object;") + strings( + "context" + ) + opcodes( + Opcode.CONST_4, + Opcode.INSTANCE_OF, + Opcode.IF_EQZ, + Opcode.MOVE_OBJECT, + Opcode.CHECK_CAST, + Opcode.IGET, + Opcode.CONST_HIGH16, + Opcode.AND_INT, + Opcode.IF_EQZ, + Opcode.SUB_INT_2ADDR, + Opcode.IPUT, + Opcode.GOTO, + Opcode.NEW_INSTANCE, + Opcode.INVOKE_DIRECT, + Opcode.IGET_OBJECT, + Opcode.SGET_OBJECT, + Opcode.IGET, + Opcode.IF_EQZ, + Opcode.IF_NE, + Opcode.INVOKE_STATIC, + Opcode.GOTO, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/VerifyingSignatureFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/VerifyingSignatureFingerprint.kt new file mode 100644 index 000000000..bac314ebf --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/integrity/fingerprints/VerifyingSignatureFingerprint.kt @@ -0,0 +1,12 @@ +package app.revanced.patches.kakaotalk.integrity.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags + +@Suppress("unused") +internal val verifyingSignatureFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC) + returns("Z") + parameters() + strings("getPackageName(...)") +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/ForceEnableDebugModePatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/ForceEnableDebugModePatch.kt new file mode 100644 index 000000000..d72ba6ed2 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/ForceEnableDebugModePatch.kt @@ -0,0 +1,31 @@ +package app.revanced.patches.kakaotalk.misc + +import app.revanced.patcher.extensions.InstructionExtensions.addInstructions +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.misc.fingerprints.configConstructorFingerprint +import com.android.tools.smali.dexlib2.Opcode + +@Suppress("unused") +val forceEnableDebugModePatch = bytecodePatch( + name = "Force enable debug mode", + description = "Enables debug mode in the app.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = configConstructorFingerprint.method + val insns = method.instructions + val idxReturn = insns.indexOfFirst { it.opcode == Opcode.RETURN_VOID } // RETURN_VOID + + val clazz = method.definingClass + + method.addInstructions( + idxReturn, + """ + const/4 v0, 0x1 + sput-boolean v0, $clazz->a:Z + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/RemoveShopTabPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/RemoveShopTabPatch.kt new file mode 100644 index 000000000..40925b368 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/RemoveShopTabPatch.kt @@ -0,0 +1,44 @@ +package app.revanced.patches.kakaotalk.misc + +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.misc.fingerprints.addNavigationTabFingerprint +import app.revanced.util.getReference +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction35c +import com.android.tools.smali.dexlib2.iface.reference.FieldReference +import com.android.tools.smali.dexlib2.iface.reference.MethodReference + +val removeShopTabPatch = bytecodePatch( + name = "Remove shop tab", + description = "Removes the shop tab from the bottom navigation bar.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val method = addNavigationTabFingerprint.method + val insns = method.instructions + + val matches = insns.mapIndexedNotNull { idx, inst -> + if (inst is BuilderInstruction35c + && inst.opcode == Opcode.INVOKE_VIRTUAL + && (inst.getReference()?.name == "add") + ) { + val prev = insns.getOrNull(idx - 1) as? BuilderInstruction21c + val fldName = (prev?.reference as? FieldReference)?.name + if (fldName == "SHOPPING_TAB" || fldName == "CALL_TAB") { + Pair(idx - 1, idx) + } else null + } else null + } + + matches + .sortedByDescending { it.second } + .forEach { (loadIdx, invokeIdx) -> + method.removeInstruction(invokeIdx) + method.removeInstruction(loadIdx) + } + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/fingerprints/ForceEnableDebugModeFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/fingerprints/ForceEnableDebugModeFingerprint.kt new file mode 100644 index 000000000..3b3206807 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/fingerprints/ForceEnableDebugModeFingerprint.kt @@ -0,0 +1,24 @@ +package app.revanced.patches.kakaotalk.misc.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val configConstructorFingerprint = fingerprint { + accessFlags(AccessFlags.STATIC, AccessFlags.CONSTRUCTOR) + returns("V") + parameters() + strings("google", "one", "getBytes(...)") + opcodes( + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.CONST_4, + Opcode.CONST_4, + Opcode.IF_EQ, + Opcode.MOVE, + Opcode.GOTO, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/fingerprints/RemoveShopTabFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/fingerprints/RemoveShopTabFingerprint.kt new file mode 100644 index 000000000..394981932 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/misc/fingerprints/RemoveShopTabFingerprint.kt @@ -0,0 +1,26 @@ +package app.revanced.patches.kakaotalk.misc.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val addNavigationTabFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + returns("V") + parameters() + strings("webtoon") + opcodes( + Opcode.IGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_INTERFACE, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.IF_EQZ, + Opcode.SGET_OBJECT, + Opcode.GOTO, + ) +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/send/AllowReplyToFeedPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/send/AllowReplyToFeedPatch.kt new file mode 100644 index 000000000..8d6dd3340 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/send/AllowReplyToFeedPatch.kt @@ -0,0 +1,48 @@ +package app.revanced.patches.kakaotalk.send + +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.removeInstruction +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patches.kakaotalk.send.fingerprints.allowSwipeReplyToFeedFingerprint +import app.revanced.patches.kakaotalk.send.fingerprints.realActionForReplyFingerprint +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction11n +import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction21t + +@Suppress("unused") +val allowReplyToFeedPatch = app.revanced.patcher.patch.bytecodePatch( + name = "Allow reply to feed", + description = "Allows replying to feed messages", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val patch: (Fingerprint) -> Unit = { fp -> + val method = fp.method + val insns = method.instructions + + val idxIfnez = insns.indexOfFirst { it is Instruction21t && it.opcode == Opcode.IF_NEZ } + val idxIfnezTarget = (insns[idxIfnez] as Instruction21t).registerA + + val idxInvoke = insns.subList(0, idxIfnez) + .indexOfLast { it.opcode == Opcode.INVOKE_VIRTUAL } + + (idxInvoke until idxIfnez).toList() + .sortedDescending() + .forEach { method.removeInstruction(it) } + + method.replaceInstruction( + idxInvoke, + BuilderInstruction11n( + Opcode.CONST_4, + idxIfnezTarget, + 0x0, + ) + ) + } + + patch(realActionForReplyFingerprint) + patch(allowSwipeReplyToFeedFingerprint) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/send/fingerprints/AllowReplyToFeedFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/send/fingerprints/AllowReplyToFeedFingerprint.kt new file mode 100644 index 000000000..b86dc6f07 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/send/fingerprints/AllowReplyToFeedFingerprint.kt @@ -0,0 +1,53 @@ +package app.revanced.patches.kakaotalk.send.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val realActionForReplyFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + strings( + "chatLog", + "chatRoom" + ) + opcodes( + Opcode.CONST_STRING, + Opcode.CONST_STRING, + Opcode.CONST, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CONST_4, + Opcode.INVOKE_DIRECT, + Opcode.IPUT_OBJECT, + Opcode.IPUT_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT, + Opcode.IF_NEZ, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT_OBJECT, + Opcode.SGET_OBJECT, + Opcode.IF_NE, + Opcode.GOTO_16 + ) +} + +internal val allowSwipeReplyToFeedFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters("Landroidx/recyclerview/widget/RecyclerView;", "Landroidx/recyclerview/widget/RecyclerView\$D;") + returns("I") + strings("recyclerView", "viewHolder") + opcodes( + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.CONST_STRING, + Opcode.INVOKE_STATIC, + Opcode.INVOKE_VIRTUAL, + Opcode.MOVE_RESULT, + Opcode.CONST_4, + Opcode.CONST_4, + ) + custom { method, classDef -> method.name == "getMovementFlags" } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/versioninfo/VersionInfoPatch.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/versioninfo/VersionInfoPatch.kt new file mode 100644 index 000000000..5cfc80f4d --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/versioninfo/VersionInfoPatch.kt @@ -0,0 +1,57 @@ +package app.revanced.patches.kakaotalk.versioninfo + +import app.revanced.patcher.Fingerprint +import app.revanced.patcher.extensions.InstructionExtensions.instructions +import app.revanced.patcher.extensions.InstructionExtensions.replaceInstruction +import app.revanced.patcher.patch.bytecodePatch +import app.revanced.patches.kakaotalk.versioninfo.fingerprints.versionInfoFingerprint +import app.revanced.patches.kakaotalk.versioninfo.fingerprints.versionInfoPreviewFingerprint +import com.android.tools.smali.dexlib2.Opcode +import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction21c +import com.android.tools.smali.dexlib2.iface.reference.StringReference +import com.android.tools.smali.dexlib2.immutable.reference.ImmutableStringReference +import java.time.format.DateTimeFormatter + +@Suppress("unused") +val versionInfoPatch = bytecodePatch( + name = "Version info patch", + description = "Patches the version info to include '(ReVanced)' in the version string.", +) { + compatibleWith("com.kakao.talk"("25.4.2")) + + execute { + val runPatch: (Fingerprint, Boolean) -> Unit = { fp, inDetail -> + val versionInfo = fp.method.instructions + .filterIsInstance() + .first { inst -> + inst.opcode == Opcode.CONST_STRING + } + + val versionString = (versionInfo.reference as StringReference).string + + fp.method + .replaceInstruction( + versionInfo.location.index, + BuilderInstruction21c( + Opcode.CONST_STRING, + versionInfo.registerA, + ImmutableStringReference( + if (inDetail) { + val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") + "$versionString (ReVanced)\nBuild at: ${ + formatter.format( + java.time.LocalDateTime.now() + ) + }" + } else { + "$versionString (ReVanced)" + } + ) + ) + ) + } + + runPatch(versionInfoFingerprint, true) + runPatch(versionInfoPreviewFingerprint, false) + } +} \ No newline at end of file diff --git a/patches/src/main/kotlin/app/revanced/patches/kakaotalk/versioninfo/fingerprints/VersionInfoFingerprint.kt b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/versioninfo/fingerprints/VersionInfoFingerprint.kt new file mode 100644 index 000000000..1d47477a5 --- /dev/null +++ b/patches/src/main/kotlin/app/revanced/patches/kakaotalk/versioninfo/fingerprints/VersionInfoFingerprint.kt @@ -0,0 +1,37 @@ +package app.revanced.patches.kakaotalk.versioninfo.fingerprints + +import app.revanced.patcher.fingerprint +import com.android.tools.smali.dexlib2.AccessFlags +import com.android.tools.smali.dexlib2.Opcode + +internal val versionInfoFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.CONSTRUCTOR) + returns("V") + opcodes( + Opcode.INVOKE_DIRECT, + Opcode.CONST_STRING, + Opcode.IPUT_OBJECT, + Opcode.IPUT_BOOLEAN, + Opcode.IPUT_OBJECT, + Opcode.RETURN_VOID, + ) +} + +internal val versionInfoPreviewFingerprint = fingerprint { + accessFlags(AccessFlags.PUBLIC, AccessFlags.FINAL) + parameters() + returns("Ljava/lang/String;") + opcodes( + Opcode.CONST_STRING, + Opcode.SGET_OBJECT, + Opcode.INVOKE_VIRTUAL, + Opcode.NEW_INSTANCE, + Opcode.CONST_4, + Opcode.CONST_4, + Opcode.INVOKE_DIRECT, + Opcode.SGET_OBJECT, + Opcode.INVOKE_STATIC, + Opcode.MOVE_RESULT_OBJECT, + Opcode.CHECK_CAST, + ) +} \ No newline at end of file