Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions patches/api/patches.api
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,10 @@ public final class app/revanced/patches/instagram/hide/navigation/HideNavigation
public static final fun getHideNavigationButtonsPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

public final class app/revanced/patches/instagram/hide/reshare/HideReshareButtonPatchKt {
public static final fun getHideReshareButtonPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}

public final class app/revanced/patches/instagram/hide/stories/HideStoriesKt {
public static final fun getHideStoriesPatch ()Lapp/revanced/patcher/patch/BytecodePatch;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package app.revanced.patches.instagram.hide.reshare

import app.revanced.patcher.fingerprint
import app.revanced.util.indexOfFirstInstruction
import com.android.tools.smali.dexlib2.AccessFlags
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.iface.instruction.formats.SparseSwitchPayload

// The hash code of the field of interest. It is used as the key of a hashmap
internal val hashedFieldInteger = "enable_media_notes_production".hashCode()

internal val feedResponseMediaParserFingerprint = fingerprint {
strings("array_out_of_bounds_exception", "null_pointer_exception", "MediaDict")
custom { method, _ ->
method.indexOfFirstInstruction {
opcode == Opcode.SPARSE_SWITCH_PAYLOAD &&
(this as SparseSwitchPayload).switchElements.any { it.key == hashedFieldInteger }
} >= 0
}
}

internal val reelPostsResponseMediaParserFingerprint = fingerprint {
accessFlags(AccessFlags.PUBLIC, AccessFlags.STATIC)
parameters("L", "L", "L", "[I")
returns("L")
custom { method, _ ->
method.indexOfFirstInstruction {
opcode == Opcode.CONST && (this as Instruction31i).narrowLiteral == hashedFieldInteger
} >= 0
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package app.revanced.patches.instagram.hide.reshare

import app.revanced.patcher.extensions.InstructionExtensions.addInstruction
import app.revanced.patcher.extensions.InstructionExtensions.getInstruction
import app.revanced.patcher.patch.bytecodePatch
import app.revanced.util.getReference
import app.revanced.util.indexOfFirstInstructionOrThrow
import com.android.tools.smali.dexlib2.Opcode
import com.android.tools.smali.dexlib2.builder.instruction.BuilderSparseSwitchPayload
import com.android.tools.smali.dexlib2.iface.instruction.OneRegisterInstruction
import com.android.tools.smali.dexlib2.iface.instruction.formats.Instruction31i
import com.android.tools.smali.dexlib2.iface.reference.MethodReference

@Suppress("unused")
val hideReshareButtonPatch = bytecodePatch(
name = "Hide reshare button",
description = "Hides the reshare button from both posts and reels.",
use = false
) {
compatibleWith("com.instagram.android")

execute {
feedResponseMediaParserFingerprint.method.apply {
// Each json field is parsed in a switch statement, where the case of the switch is the hashed field name.

/**
* First, find the switch payload where our field of interest is being processed. So find the payload that
* has a key == to our field of interest.
*/
val switchPayload = implementation!!.instructions.first { ins ->
ins.opcode == Opcode.SPARSE_SWITCH_PAYLOAD &&
(ins as BuilderSparseSwitchPayload).switchElements.any { it.key == hashedFieldInteger }
} as BuilderSparseSwitchPayload

// Get the target label, so find the instruction offset where the switch case is pointing to
val switchTargetLabel = switchPayload.switchElements
.first { it.key == hashedFieldInteger }
.target

// From that label, navigate forward until our field os interest is being instantiated
val moveResultIndex = indexOfFirstInstructionOrThrow(
switchTargetLabel.location.index,
Opcode.MOVE_RESULT_OBJECT
)

// Get its register
val moveResultRegister = getInstruction<OneRegisterInstruction>(moveResultIndex).registerA

// Override it
addInstruction(
moveResultIndex + 1,
"sget-object v$moveResultRegister, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;"
)
}

reelPostsResponseMediaParserFingerprint.method.apply {
// Each json field is parsed in a series of if statements, where the if comparison is done comparing the hashed field name.

// Get index of "const v*, -0x207dadd2"
val switchIndex = indexOfFirstInstructionOrThrow {
opcode == Opcode.CONST && (this as Instruction31i).narrowLiteral == hashedFieldInteger
}

// Here an internal Instagram native library is used to handle settings of experiments, using hash codes of the fields
// Find where our field of interest is being instantiated
val moveBooleanResultIndex = indexOfFirstInstructionOrThrow(switchIndex) {
getReference<MethodReference>()?.name == "getOptionalBooleanValueByHashCode"
} + 1

// Its register
val moveBooleanResultRegister = getInstruction<OneRegisterInstruction>(moveBooleanResultIndex).registerA

// Override
addInstruction(
moveBooleanResultIndex,
"sget-object v$moveBooleanResultRegister, Ljava/lang/Boolean;->FALSE:Ljava/lang/Boolean;"
)
}
}
}
Loading