|
1 | 1 | package app.revanced.patches.duolingo.unlocksuper |
2 | 2 |
|
3 | | -import app.revanced.patcher.data.BytecodeContext |
4 | 3 | import app.revanced.patcher.extensions.InstructionExtensions.addInstructions |
5 | 4 | import app.revanced.patcher.extensions.InstructionExtensions.getInstruction |
6 | | -import app.revanced.patcher.extensions.InstructionExtensions.getInstructions |
7 | | -import app.revanced.patcher.patch.BytecodePatch |
| 5 | +import app.revanced.patcher.extensions.InstructionExtensions.instructions |
8 | 6 | import app.revanced.patcher.patch.PatchException |
9 | | -import app.revanced.patcher.patch.annotation.CompatiblePackage |
10 | | -import app.revanced.patcher.patch.annotation.Patch |
| 7 | +import app.revanced.patcher.patch.bytecodePatch |
11 | 8 | import app.revanced.patcher.util.proxy.mutableTypes.MutableMethod |
12 | | -import app.revanced.patches.duolingo.unlocksuper.fingerprints.IsUserSuperMethodFingerprint |
13 | | -import app.revanced.patches.duolingo.unlocksuper.fingerprints.UserSerializationMethodFingerprint |
14 | | -import app.revanced.util.exception |
| 9 | +import app.revanced.patches.duolingo.unlocksuper.fingerprints.isUserSuperMethodFingerprint |
| 10 | +import app.revanced.patches.duolingo.unlocksuper.fingerprints.userSerializationMethodFingerprint |
15 | 11 | import com.android.tools.smali.dexlib2.Opcode |
16 | 12 | import com.android.tools.smali.dexlib2.builder.instruction.BuilderInstruction22c |
17 | 13 | import com.android.tools.smali.dexlib2.iface.instruction.TwoRegisterInstruction |
18 | 14 | import com.android.tools.smali.dexlib2.iface.reference.Reference |
19 | 15 |
|
20 | | -@Patch( |
| 16 | +@Suppress("unused") |
| 17 | +val unlockDuolingoSuperPatch = bytecodePatch( |
21 | 18 | name = "Unlock Duolingo Super", |
22 | 19 | description = "Unlocks Duolingo Super features by patching the user serialization to always enable Super status.", |
23 | | - compatiblePackages = [CompatiblePackage("com.duolingo", ["6.51.4"])] |
24 | | -) |
25 | | -@Suppress("unused") |
26 | | -object UnlockDuolingoSuperPatch : BytecodePatch( |
27 | | - setOf( |
28 | | - UserSerializationMethodFingerprint, |
29 | | - IsUserSuperMethodFingerprint |
30 | | - ) |
31 | 20 | ) { |
32 | | - /* First find the reference to the isUserSuper field, then patch the instruction that assigns it to false. |
33 | | - * This strategy is used because the method that sets the isUserSuper field is difficult to fingerprint reliably. |
34 | | - * Note: In version 6.51.4, this patch may not be needed if the premium patch handles all features. |
35 | | - */ |
36 | | - override fun execute(context: BytecodeContext) { |
| 21 | + compatibleWith("com.duolingo"("6.51.4")) |
| 22 | + |
| 23 | + execute { |
| 24 | + /* First find the reference to the isUserSuper field, then patch the instruction that assigns it to false. |
| 25 | + * This strategy is used because the method that sets the isUserSuper field is difficult to fingerprint reliably. |
| 26 | + * Note: In version 6.51.4, this patch may not be needed if the premium patch handles all features. |
| 27 | + */ |
| 28 | + |
37 | 29 | // Find the reference to the isUserSuper field. |
38 | | - val isUserSuperReference = IsUserSuperMethodFingerprint |
39 | | - .result |
40 | | - ?.mutableMethod |
41 | | - ?.getInstructions() |
42 | | - ?.filterIsInstance<BuilderInstruction22c>() |
43 | | - ?.firstOrNull { it.opcode == Opcode.IGET_BOOLEAN } |
| 30 | + val isUserSuperReference = isUserSuperMethodFingerprint |
| 31 | + .method |
| 32 | + .instructions |
| 33 | + .filterIsInstance<BuilderInstruction22c>() |
| 34 | + .firstOrNull { it.opcode == Opcode.IGET_BOOLEAN } |
44 | 35 | ?.reference |
45 | | - ?: throw IsUserSuperMethodFingerprint.exception |
| 36 | + ?: throw PatchException("Could not find isUserSuper field reference") |
46 | 37 |
|
47 | 38 | // Patch the instruction that assigns isUserSuper to true. |
48 | | - UserSerializationMethodFingerprint |
49 | | - .result |
50 | | - ?.mutableMethod |
51 | | - ?.apply { |
52 | | - val assignIndex = indexOfReference(isUserSuperReference) |
53 | | - val assignInstruction = getInstruction<TwoRegisterInstruction>(assignIndex) |
| 39 | + userSerializationMethodFingerprint.method.apply { |
| 40 | + val assignIndex = indexOfReference(isUserSuperReference) |
| 41 | + val assignInstruction = getInstruction<TwoRegisterInstruction>(assignIndex) |
54 | 42 |
|
55 | | - // add an instruction to force the value to `true`. ideally we'd replace the existing |
56 | | - // instruction, but there's an `if` block above with different paths based on various |
57 | | - // states (i.e. subscription vs super vs gold or whatever), and I don't think it's |
58 | | - // worth removing the entire statement. |
59 | | - addInstructions( |
60 | | - assignIndex + 1, |
61 | | - """ |
62 | | - const/4 v${assignInstruction.registerA}, 0x1 |
63 | | - iput-boolean v${assignInstruction.registerA}, v0, $isUserSuperReference |
64 | | - """.trimIndent() |
65 | | - ) |
66 | | - } |
67 | | - ?: throw UserSerializationMethodFingerprint.exception |
68 | | - } |
69 | | - |
70 | | - private fun MutableMethod.indexOfReference(reference: Reference) = getInstructions() |
71 | | - .indexOfFirst { it is BuilderInstruction22c && it.opcode == Opcode.IPUT_BOOLEAN && it.reference == reference } |
72 | | - .let { |
73 | | - if (it == -1) throw PatchException("Could not find index of instruction with supplied reference.") |
74 | | - else it |
| 43 | + // add an instruction to force the value to `true`. ideally we'd replace the existing |
| 44 | + // instruction, but there's an `if` block above with different paths based on various |
| 45 | + // states (i.e. subscription vs super vs gold or whatever), and I don't think it's |
| 46 | + // worth removing the entire statement. |
| 47 | + addInstructions( |
| 48 | + assignIndex + 1, |
| 49 | + """ |
| 50 | + const/4 v${assignInstruction.registerA}, 0x1 |
| 51 | + iput-boolean v${assignInstruction.registerA}, v0, $isUserSuperReference |
| 52 | + """.trimIndent() |
| 53 | + ) |
75 | 54 | } |
| 55 | + } |
76 | 56 | } |
| 57 | + |
| 58 | +private fun MutableMethod.indexOfReference(reference: Reference) = instructions |
| 59 | + .indexOfFirst { it is BuilderInstruction22c && it.opcode == Opcode.IPUT_BOOLEAN && it.reference == reference } |
| 60 | + .let { |
| 61 | + if (it == -1) throw PatchException("Could not find index of instruction with supplied reference.") |
| 62 | + else it |
| 63 | + } |
0 commit comments