feat(Duolingo): Add Unlock Duolingo Super
patch (#2862)
This commit is contained in:
parent
0c9bf8ae1d
commit
61a7533136
|
@ -0,0 +1,14 @@
|
||||||
|
package app.revanced.patches.duolingo.unlocksuper.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
object IsUserSuperMethodFingerprint : MethodFingerprint(
|
||||||
|
returnType = "Ljava/lang/Object",
|
||||||
|
parameters = listOf("Ljava/lang/Object"),
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.FINAL,
|
||||||
|
strings = listOf("user"),
|
||||||
|
opcodes = listOf(Opcode.IGET_BOOLEAN),
|
||||||
|
)
|
|
@ -0,0 +1,20 @@
|
||||||
|
package app.revanced.patches.duolingo.unlocksuper.fingerprints
|
||||||
|
|
||||||
|
import app.revanced.patcher.extensions.or
|
||||||
|
import app.revanced.patcher.fingerprint.method.impl.MethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.AccessFlags
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
|
||||||
|
object UserSerializationMethodFingerprint : MethodFingerprint(
|
||||||
|
returnType = "V",
|
||||||
|
accessFlags = AccessFlags.PUBLIC or AccessFlags.CONSTRUCTOR,
|
||||||
|
strings = listOf(
|
||||||
|
"betaStatus",
|
||||||
|
"coachOutfit",
|
||||||
|
"globalAmbassadorStatus",
|
||||||
|
),
|
||||||
|
opcodes = listOf(
|
||||||
|
Opcode.MOVE_FROM16,
|
||||||
|
Opcode.IPUT_BOOLEAN,
|
||||||
|
),
|
||||||
|
)
|
|
@ -0,0 +1,64 @@
|
||||||
|
package app.revanced.patches.duolingo.unlocksuper.patch
|
||||||
|
|
||||||
|
import app.revanced.extensions.toErrorResult
|
||||||
|
import app.revanced.patcher.annotation.Compatibility
|
||||||
|
import app.revanced.patcher.annotation.Description
|
||||||
|
import app.revanced.patcher.annotation.Name
|
||||||
|
import app.revanced.patcher.annotation.Package
|
||||||
|
import app.revanced.patcher.data.BytecodeContext
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.getInstructions
|
||||||
|
import app.revanced.patcher.extensions.InstructionExtensions.replaceInstructions
|
||||||
|
import app.revanced.patcher.patch.BytecodePatch
|
||||||
|
import app.revanced.patcher.patch.PatchException
|
||||||
|
import app.revanced.patcher.patch.annotations.Patch
|
||||||
|
import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod
|
||||||
|
import app.revanced.patches.duolingo.unlocksuper.fingerprints.IsUserSuperMethodFingerprint
|
||||||
|
import app.revanced.patches.duolingo.unlocksuper.fingerprints.UserSerializationMethodFingerprint
|
||||||
|
import com.android.tools.smali.dexlib2.Opcode
|
||||||
|
import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22c
|
||||||
|
import com.android.tools.smali.dexlib2.iface.reference.Reference
|
||||||
|
|
||||||
|
@Patch
|
||||||
|
@Name("Unlock Duolingo Super")
|
||||||
|
@Description("Unlocks Duolingo Super features.")
|
||||||
|
@Compatibility([Package("com.duolingo")])
|
||||||
|
class UnlockDuolingoSuperPatch : BytecodePatch(
|
||||||
|
listOf(UserSerializationMethodFingerprint, IsUserSuperMethodFingerprint)
|
||||||
|
) {
|
||||||
|
|
||||||
|
/* First find the reference to the isUserSuper field, then patch the instruction that assigns it to false.
|
||||||
|
* This strategy is used because the method that sets the isUserSuper field is difficult to fingerprint reliably.
|
||||||
|
*/
|
||||||
|
override fun execute(context: BytecodeContext) {
|
||||||
|
// Find the reference to the isUserSuper field.
|
||||||
|
val isUserSuperReference = IsUserSuperMethodFingerprint
|
||||||
|
.result
|
||||||
|
?.mutableMethod
|
||||||
|
?.getInstructions()
|
||||||
|
?.filterIsInstance<BuilderInstruction22c>()
|
||||||
|
?.firstOrNull { it.opcode == Opcode.IGET_BOOLEAN }
|
||||||
|
?.reference
|
||||||
|
?: throw IsUserSuperMethodFingerprint.toErrorResult()
|
||||||
|
|
||||||
|
// Patch the instruction that assigns isUserSuper to true.
|
||||||
|
UserSerializationMethodFingerprint
|
||||||
|
.result
|
||||||
|
?.mutableMethod
|
||||||
|
?.apply {
|
||||||
|
replaceInstructions(
|
||||||
|
indexOfReference(isUserSuperReference) - 1,
|
||||||
|
"const/4 v2, 0x1"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
?: throw UserSerializationMethodFingerprint.toErrorResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
private fun MutableMethod.indexOfReference(reference: Reference) = getInstructions()
|
||||||
|
.filterIsInstance<BuilderInstruction22c>()
|
||||||
|
.filter { it.opcode == Opcode.IPUT_BOOLEAN }.indexOfFirst { it.reference == reference }.let {
|
||||||
|
if (it == -1) throw PatchException("Could not find index of instruction with supplied reference.")
|
||||||
|
else it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue