Merge pull request 'feat: initial kakaotalk patches' (#3) from feat/kakaotalk into main

Reviewed-on: #3
This commit is contained in:
2025-05-23 09:12:50 +09:00
36 changed files with 1233 additions and 0 deletions

View File

@ -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;
}

View File

@ -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()
)
}
}

View File

@ -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()
)
}
}

View File

@ -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<MethodReference>()?.name == "add")
) {
val prev = addSectionToMoreTabUIInsns.getOrNull(idx - 1) as? BuilderInstruction35c
val ref = (prev?.getReference<MethodReference>())
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)
}
}
}

View File

@ -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()
)
}
}

View File

@ -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()
)
}
}

View File

@ -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
)
}

View File

@ -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,
)
}

View File

@ -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" }
}

View File

@ -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" }
}

View File

@ -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,
)
}

View File

@ -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()
)
}
}

View File

@ -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
}
}

View File

@ -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<BuilderInstruction22t>()
.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)
}
}
}

View File

@ -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")
}

View File

@ -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<BuilderInstruction22t>()
.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<BuilderInstruction22t>()
.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)
}
}
}

View File

@ -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+")
}

View File

@ -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"
}
}

View File

@ -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()
)
}
}

View File

@ -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,
)
}

View File

@ -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()
)
}
}

View File

@ -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
)
}

View File

@ -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<BuilderInstruction21c>()
.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<Instruction>()
insns.forEachIndexed { i, inst ->
if (inst is BuilderInstruction35c &&
inst.opcode == Opcode.INVOKE_VIRTUAL &&
(inst.getReference<MethodReference>()?.name == postprocessMoatCheckFailedMethod.name) &&
inst.getReference<MethodReference>()?.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)
}
}

View File

@ -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()
)
}
}

View File

@ -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()
)
}
}

View File

@ -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", "<anonymous parameter 1>", "<anonymous parameter 2>")
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", "<anonymous parameter 1>", "<anonymous parameter 2>")
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")
}

View File

@ -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,
)
}

View File

@ -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(...)")
}

View File

@ -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()
)
}
}

View File

@ -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<MethodReference>()?.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)
}
}
}

View File

@ -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,
)
}

View File

@ -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,
)
}

View File

@ -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)
}
}

View File

@ -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" }
}

View File

@ -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<BuilderInstruction21c>()
.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)
}
}

View File

@ -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,
)
}