mirror of
https://github.com/ReVanced/revanced-patcher.git
synced 2024-11-10 01:02:22 +01:00
feat: add inline smali compiler
This commit is contained in:
parent
a1b6b06bd3
commit
bfe4e3e298
4 changed files with 361 additions and 22 deletions
|
@ -12,9 +12,10 @@ repositories {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation(kotlin("stdlib"))
|
implementation(kotlin("stdlib"))
|
||||||
implementation("com.github.lanchon.dexpatcher:multidexlib2:2.3.4")
|
|
||||||
implementation("io.github.microutils:kotlin-logging:2.1.21")
|
implementation("com.github.lanchon.dexpatcher:multidexlib2:2.3.4.r2")
|
||||||
testImplementation("ch.qos.logback:logback-classic:1.2.11") // use your own logger!
|
implementation("org.smali:smali:2.3.4")
|
||||||
|
|
||||||
testImplementation(kotlin("test"))
|
testImplementation(kotlin("test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package app.revanced.patcher.smali
|
||||||
|
|
||||||
|
import org.antlr.runtime.CommonTokenStream
|
||||||
|
import org.antlr.runtime.TokenSource
|
||||||
|
import org.antlr.runtime.tree.CommonTreeNodeStream
|
||||||
|
import org.jf.dexlib2.Opcodes
|
||||||
|
import org.jf.dexlib2.builder.BuilderInstruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.writer.builder.DexBuilder
|
||||||
|
import org.jf.smali.LexerErrorInterface
|
||||||
|
import org.jf.smali.smaliFlexLexer
|
||||||
|
import org.jf.smali.smaliParser
|
||||||
|
import org.jf.smali.smaliTreeWalker
|
||||||
|
import java.io.InputStreamReader
|
||||||
|
|
||||||
|
private const val METHOD_TEMPLATE = """
|
||||||
|
.class public Linlinecompiler;
|
||||||
|
.super Ljava/lang/Object;
|
||||||
|
.method public static compiler()V
|
||||||
|
.registers 1
|
||||||
|
%s
|
||||||
|
.end method
|
||||||
|
"""
|
||||||
|
|
||||||
|
class InlineSmaliCompiler {
|
||||||
|
companion object {
|
||||||
|
/**
|
||||||
|
* Compiles a string of Smali code to a list of instructions.
|
||||||
|
* Do not cross the boundaries of the control flow (if-nez insn, etc),
|
||||||
|
* as that will result in exceptions since the labels cannot be calculated.
|
||||||
|
* Do not create dummy labels to fix the issue, since the code addresses will
|
||||||
|
* be messed up and results in broken Dalvik bytecode.
|
||||||
|
*/
|
||||||
|
fun compileMethodInstructions(instructions: String): List<BuilderInstruction> {
|
||||||
|
val input = METHOD_TEMPLATE.format(instructions)
|
||||||
|
val reader = InputStreamReader(input.byteInputStream())
|
||||||
|
val lexer: LexerErrorInterface = smaliFlexLexer(reader, 15)
|
||||||
|
val tokens = CommonTokenStream(lexer as TokenSource)
|
||||||
|
val parser = smaliParser(tokens)
|
||||||
|
val result = parser.smali_file()
|
||||||
|
if (parser.numberOfSyntaxErrors > 0 || lexer.numberOfSyntaxErrors > 0) {
|
||||||
|
throw IllegalStateException(
|
||||||
|
"Encountered ${parser.numberOfSyntaxErrors} parser syntax errors and ${lexer.numberOfSyntaxErrors} lexer syntax errors!"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val treeStream = CommonTreeNodeStream(result.tree)
|
||||||
|
treeStream.tokenStream = tokens
|
||||||
|
val dexGen = smaliTreeWalker(treeStream)
|
||||||
|
dexGen.setDexBuilder(DexBuilder(Opcodes.getDefault()))
|
||||||
|
val classDef = dexGen.smali_file()
|
||||||
|
return classDef.methods.first().implementation!!.instructions.map { it.toBuilderInstruction() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.asInstructions() = InlineSmaliCompiler.compileMethodInstructions(this)
|
||||||
|
fun String.asInstruction() = this.asInstructions().first()
|
|
@ -0,0 +1,261 @@
|
||||||
|
package app.revanced.patcher.smali
|
||||||
|
|
||||||
|
import org.jf.dexlib2.Format
|
||||||
|
import org.jf.dexlib2.builder.instruction.*
|
||||||
|
import org.jf.dexlib2.iface.instruction.Instruction
|
||||||
|
import org.jf.dexlib2.iface.instruction.formats.*
|
||||||
|
import org.jf.util.ExceptionWithContext
|
||||||
|
|
||||||
|
fun Instruction.toBuilderInstruction() =
|
||||||
|
when (this.opcode.format) {
|
||||||
|
Format.Format10x -> InstructionConverter.newBuilderInstruction10x(this as Instruction10x)
|
||||||
|
Format.Format11n -> InstructionConverter.newBuilderInstruction11n(this as Instruction11n)
|
||||||
|
Format.Format11x -> InstructionConverter.newBuilderInstruction11x(this as Instruction11x)
|
||||||
|
Format.Format12x -> InstructionConverter.newBuilderInstruction12x(this as Instruction12x)
|
||||||
|
Format.Format20bc -> InstructionConverter.newBuilderInstruction20bc(this as Instruction20bc)
|
||||||
|
Format.Format21c -> InstructionConverter.newBuilderInstruction21c(this as Instruction21c)
|
||||||
|
Format.Format21ih -> InstructionConverter.newBuilderInstruction21ih(this as Instruction21ih)
|
||||||
|
Format.Format21lh -> InstructionConverter.newBuilderInstruction21lh(this as Instruction21lh)
|
||||||
|
Format.Format21s -> InstructionConverter.newBuilderInstruction21s(this as Instruction21s)
|
||||||
|
Format.Format22b -> InstructionConverter.newBuilderInstruction22b(this as Instruction22b)
|
||||||
|
Format.Format22c -> InstructionConverter.newBuilderInstruction22c(this as Instruction22c)
|
||||||
|
Format.Format22cs -> InstructionConverter.newBuilderInstruction22cs(this as Instruction22cs)
|
||||||
|
Format.Format22s -> InstructionConverter.newBuilderInstruction22s(this as Instruction22s)
|
||||||
|
Format.Format22x -> InstructionConverter.newBuilderInstruction22x(this as Instruction22x)
|
||||||
|
Format.Format23x -> InstructionConverter.newBuilderInstruction23x(this as Instruction23x)
|
||||||
|
Format.Format31c -> InstructionConverter.newBuilderInstruction31c(this as Instruction31c)
|
||||||
|
Format.Format31i -> InstructionConverter.newBuilderInstruction31i(this as Instruction31i)
|
||||||
|
Format.Format32x -> InstructionConverter.newBuilderInstruction32x(this as Instruction32x)
|
||||||
|
Format.Format35c -> InstructionConverter.newBuilderInstruction35c(this as Instruction35c)
|
||||||
|
Format.Format35mi -> InstructionConverter.newBuilderInstruction35mi(this as Instruction35mi)
|
||||||
|
Format.Format35ms -> InstructionConverter.newBuilderInstruction35ms(this as Instruction35ms)
|
||||||
|
Format.Format3rc -> InstructionConverter.newBuilderInstruction3rc(this as Instruction3rc)
|
||||||
|
Format.Format3rmi -> InstructionConverter.newBuilderInstruction3rmi(this as Instruction3rmi)
|
||||||
|
Format.Format3rms -> InstructionConverter.newBuilderInstruction3rms(this as Instruction3rms)
|
||||||
|
Format.Format51l -> InstructionConverter.newBuilderInstruction51l(this as Instruction51l)
|
||||||
|
else -> throw ExceptionWithContext("Instruction format %s not supported", this.opcode.format)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class InstructionConverter {
|
||||||
|
companion object {
|
||||||
|
internal fun newBuilderInstruction10x(instruction: Instruction10x): BuilderInstruction10x {
|
||||||
|
return BuilderInstruction10x(
|
||||||
|
instruction.opcode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction11n(instruction: Instruction11n): BuilderInstruction11n {
|
||||||
|
return BuilderInstruction11n(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.narrowLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction11x(instruction: Instruction11x): BuilderInstruction11x {
|
||||||
|
return BuilderInstruction11x(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction12x(instruction: Instruction12x): BuilderInstruction12x {
|
||||||
|
return BuilderInstruction12x(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction20bc(instruction: Instruction20bc): BuilderInstruction20bc {
|
||||||
|
return BuilderInstruction20bc(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.verificationError,
|
||||||
|
instruction.reference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction21c(instruction: Instruction21c): BuilderInstruction21c {
|
||||||
|
return BuilderInstruction21c(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.reference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction21ih(instruction: Instruction21ih): BuilderInstruction21ih {
|
||||||
|
return BuilderInstruction21ih(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.narrowLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction21lh(instruction: Instruction21lh): BuilderInstruction21lh {
|
||||||
|
return BuilderInstruction21lh(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.wideLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction21s(instruction: Instruction21s): BuilderInstruction21s {
|
||||||
|
return BuilderInstruction21s(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.narrowLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction22b(instruction: Instruction22b): BuilderInstruction22b {
|
||||||
|
return BuilderInstruction22b(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB,
|
||||||
|
instruction.narrowLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction22c(instruction: Instruction22c): BuilderInstruction22c {
|
||||||
|
return BuilderInstruction22c(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB,
|
||||||
|
instruction.reference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction22cs(instruction: Instruction22cs): BuilderInstruction22cs {
|
||||||
|
return BuilderInstruction22cs(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB,
|
||||||
|
instruction.fieldOffset
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction22s(instruction: Instruction22s): BuilderInstruction22s {
|
||||||
|
return BuilderInstruction22s(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB,
|
||||||
|
instruction.narrowLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction22x(instruction: Instruction22x): BuilderInstruction22x {
|
||||||
|
return BuilderInstruction22x(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction23x(instruction: Instruction23x): BuilderInstruction23x {
|
||||||
|
return BuilderInstruction23x(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB,
|
||||||
|
instruction.registerC
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction31c(instruction: Instruction31c): BuilderInstruction31c {
|
||||||
|
return BuilderInstruction31c(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.reference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction31i(instruction: Instruction31i): BuilderInstruction31i {
|
||||||
|
return BuilderInstruction31i(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.narrowLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction32x(instruction: Instruction32x): BuilderInstruction32x {
|
||||||
|
return BuilderInstruction32x(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.registerB
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction35c(instruction: Instruction35c): BuilderInstruction35c {
|
||||||
|
return BuilderInstruction35c(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerCount,
|
||||||
|
instruction.registerC,
|
||||||
|
instruction.registerD,
|
||||||
|
instruction.registerE,
|
||||||
|
instruction.registerF,
|
||||||
|
instruction.registerG,
|
||||||
|
instruction.reference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction35mi(instruction: Instruction35mi): BuilderInstruction35mi {
|
||||||
|
return BuilderInstruction35mi(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerCount,
|
||||||
|
instruction.registerC,
|
||||||
|
instruction.registerD,
|
||||||
|
instruction.registerE,
|
||||||
|
instruction.registerF,
|
||||||
|
instruction.registerG,
|
||||||
|
instruction.inlineIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction35ms(instruction: Instruction35ms): BuilderInstruction35ms {
|
||||||
|
return BuilderInstruction35ms(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerCount,
|
||||||
|
instruction.registerC,
|
||||||
|
instruction.registerD,
|
||||||
|
instruction.registerE,
|
||||||
|
instruction.registerF,
|
||||||
|
instruction.registerG,
|
||||||
|
instruction.vtableIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction3rc(instruction: Instruction3rc): BuilderInstruction3rc {
|
||||||
|
return BuilderInstruction3rc(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.startRegister,
|
||||||
|
instruction.registerCount,
|
||||||
|
instruction.reference
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction3rmi(instruction: Instruction3rmi): BuilderInstruction3rmi {
|
||||||
|
return BuilderInstruction3rmi(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.startRegister,
|
||||||
|
instruction.registerCount,
|
||||||
|
instruction.inlineIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction3rms(instruction: Instruction3rms): BuilderInstruction3rms {
|
||||||
|
return BuilderInstruction3rms(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.startRegister,
|
||||||
|
instruction.registerCount,
|
||||||
|
instruction.vtableIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun newBuilderInstruction51l(instruction: Instruction51l): BuilderInstruction51l {
|
||||||
|
return BuilderInstruction51l(
|
||||||
|
instruction.opcode,
|
||||||
|
instruction.registerA,
|
||||||
|
instruction.wideLiteral
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,11 +8,15 @@ import app.revanced.patcher.patch.PatchResult
|
||||||
import app.revanced.patcher.patch.PatchResultError
|
import app.revanced.patcher.patch.PatchResultError
|
||||||
import app.revanced.patcher.patch.PatchResultSuccess
|
import app.revanced.patcher.patch.PatchResultSuccess
|
||||||
import app.revanced.patcher.signature.MethodSignature
|
import app.revanced.patcher.signature.MethodSignature
|
||||||
|
import app.revanced.patcher.smali.asInstruction
|
||||||
import org.jf.dexlib2.AccessFlags
|
import org.jf.dexlib2.AccessFlags
|
||||||
import org.jf.dexlib2.Opcode
|
import org.jf.dexlib2.Opcode
|
||||||
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
|
import org.jf.dexlib2.builder.instruction.BuilderInstruction35c
|
||||||
|
import org.jf.dexlib2.iface.reference.MethodReference
|
||||||
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
|
import org.jf.dexlib2.immutable.reference.ImmutableMethodReference
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import kotlin.test.assertContentEquals
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val signatures = arrayOf(
|
val signatures = arrayOf(
|
||||||
|
@ -43,33 +47,49 @@ fun main() {
|
||||||
?: return PatchResultError("Class 'XAdRemover' could not be found")
|
?: return PatchResultError("Class 'XAdRemover' could not be found")
|
||||||
|
|
||||||
val xAdRemoverClass = proxy.resolve()
|
val xAdRemoverClass = proxy.resolve()
|
||||||
val hideReelMethod = xAdRemoverClass.methods.single { method -> method.name.contains("HideReel") }
|
val hideReelMethod = xAdRemoverClass.methods.find {
|
||||||
|
it.name.contains("HideReel")
|
||||||
|
}!!
|
||||||
|
|
||||||
val readSettingsMethodRef = ImmutableMethodReference(
|
val instructions = hideReelMethod.implementation!!
|
||||||
"Lfi/razerman/youtube/XGlobals;",
|
|
||||||
"ReadSettings",
|
|
||||||
emptyList(),
|
|
||||||
"V"
|
|
||||||
)
|
|
||||||
|
|
||||||
val instructions = hideReelMethod.implementation!!.instructions
|
val readInsn =
|
||||||
|
"invoke-static { }, Lfi/razerman/youtube/XGlobals;->ReadSettings()V"
|
||||||
val readSettingsInstruction = BuilderInstruction35c(
|
.asInstruction() as BuilderInstruction35c
|
||||||
|
val testInsn = BuilderInstruction35c(
|
||||||
Opcode.INVOKE_STATIC,
|
Opcode.INVOKE_STATIC,
|
||||||
0,
|
0, 0, 0, 0, 0, 0,
|
||||||
0,
|
ImmutableMethodReference(
|
||||||
0,
|
"Lfi/razerman/youtube/XGlobals;",
|
||||||
0,
|
"ReadSettings",
|
||||||
0,
|
emptyList(),
|
||||||
0,
|
"V"
|
||||||
readSettingsMethodRef
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
assertEquals(testInsn.opcode, readInsn.opcode)
|
||||||
|
assertEquals(testInsn.referenceType, readInsn.referenceType)
|
||||||
|
assertEquals(testInsn.registerCount, readInsn.registerCount)
|
||||||
|
assertEquals(testInsn.registerC, readInsn.registerC)
|
||||||
|
assertEquals(testInsn.registerD, readInsn.registerD)
|
||||||
|
assertEquals(testInsn.registerE, readInsn.registerE)
|
||||||
|
assertEquals(testInsn.registerF, readInsn.registerF)
|
||||||
|
assertEquals(testInsn.registerG, readInsn.registerG)
|
||||||
|
run {
|
||||||
|
val tref = testInsn.reference as MethodReference
|
||||||
|
val rref = readInsn.reference as MethodReference
|
||||||
|
|
||||||
|
assertEquals(tref.name, rref.name)
|
||||||
|
assertEquals(tref.definingClass, rref.definingClass)
|
||||||
|
assertEquals(tref.returnType, rref.returnType)
|
||||||
|
assertContentEquals(tref.parameterTypes, rref.parameterTypes)
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: figure out control flow
|
// TODO: figure out control flow
|
||||||
// otherwise the we would still jump over to the original instruction at index 21 instead to our new one
|
// otherwise the we would still jump over to the original instruction at index 21 instead to our new one
|
||||||
instructions.add(
|
instructions.addInstruction(
|
||||||
21,
|
21,
|
||||||
readSettingsInstruction
|
readInsn
|
||||||
)
|
)
|
||||||
return PatchResultSuccess()
|
return PatchResultSuccess()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue