From a9e4e8ac3203bdd62abcd1e366f08a2269919571 Mon Sep 17 00:00:00 2001 From: oSumAtrIX Date: Wed, 6 Apr 2022 23:10:52 +0200 Subject: [PATCH] feat: Finish first patcher test --- .../proxy/mutableTypes/MutableMethod.kt | 2 +- .../app/revanced/patcher/PatcherTest.kt | 136 ++++++++++++------ src/test/resources/test1.dex | Bin 0 -> 744 bytes src/test/resources/test1.jar | Bin 758 -> 0 bytes src/test/resources/test2.dex | Bin 0 -> 916 bytes src/test/resources/test2.jar | Bin 1095 -> 0 bytes 6 files changed, 91 insertions(+), 47 deletions(-) create mode 100644 src/test/resources/test1.dex delete mode 100644 src/test/resources/test1.jar create mode 100644 src/test/resources/test2.dex delete mode 100644 src/test/resources/test2.jar diff --git a/src/main/kotlin/app/revanced/patcher/proxy/mutableTypes/MutableMethod.kt b/src/main/kotlin/app/revanced/patcher/proxy/mutableTypes/MutableMethod.kt index b35a52d..cf721e5 100644 --- a/src/main/kotlin/app/revanced/patcher/proxy/mutableTypes/MutableMethod.kt +++ b/src/main/kotlin/app/revanced/patcher/proxy/mutableTypes/MutableMethod.kt @@ -55,4 +55,4 @@ class MutableMethod(method: Method) : Method, BaseMethodReference() { return MutableMethod(this) } } -} +} \ No newline at end of file diff --git a/src/test/kotlin/app/revanced/patcher/PatcherTest.kt b/src/test/kotlin/app/revanced/patcher/PatcherTest.kt index 9987085..0a2cedf 100644 --- a/src/test/kotlin/app/revanced/patcher/PatcherTest.kt +++ b/src/test/kotlin/app/revanced/patcher/PatcherTest.kt @@ -1,16 +1,21 @@ package app.revanced.patcher import app.revanced.patcher.cache.Cache +import app.revanced.patcher.extensions.addInstructions import app.revanced.patcher.extensions.or import app.revanced.patcher.patch.Patch import app.revanced.patcher.patch.PatchResult import app.revanced.patcher.patch.PatchResultSuccess +import app.revanced.patcher.proxy.mutableTypes.MutableMethod.Companion.toMutable import app.revanced.patcher.signature.MethodSignature +import app.revanced.patcher.smali.asInstructions +import com.google.common.collect.ImmutableList import org.jf.dexlib2.AccessFlags import org.jf.dexlib2.Opcode +import org.jf.dexlib2.builder.instruction.BuilderInstruction11x import org.jf.dexlib2.builder.instruction.BuilderInstruction21c -import org.jf.dexlib2.builder.instruction.BuilderInstruction35c -import org.jf.dexlib2.immutable.reference.ImmutableMethodReference +import org.jf.dexlib2.immutable.ImmutableMethod +import org.jf.dexlib2.immutable.ImmutableMethodImplementation import org.jf.dexlib2.immutable.reference.ImmutableStringReference import org.junit.jupiter.api.Test import java.io.File @@ -35,8 +40,8 @@ internal class PatcherTest { @Test fun testPatcher() { val patcher = Patcher( - File(PatcherTest::class.java.getResource("/test1.apk")!!.toURI()), - File("/"), + File(PatcherTest::class.java.getResource("/test1.dex")!!.toURI()), + File("."), testSignatures ) @@ -44,7 +49,7 @@ internal class PatcherTest { object : Patch("TestPatch") { override fun execute(cache: Cache): PatchResult { // Get the result from the resolver cache - val result = cache.resolvedMethods["main-method"] + val result = cache.methodMap["main-method"] // Get the implementation for the resolved method val implementation = result.resolveAndGetMethod().implementation!! // Let's modify it, so it prints "Hello, ReVanced! Editing bytecode." @@ -52,47 +57,86 @@ internal class PatcherTest { // This will be the index of the instruction with the opcode CONST_STRING. val startIndex = result.scanData.startIndex - // the instruction format can be found via the docs at https://source.android.com/devices/tech/dalvik/dalvik-bytecode - // in our case we want an instruction with the opcode CONST_STRING and the string "Hello, ReVanced! Editing bytecode." - // the format is 21c, so we create a new BuilderInstruction21c - // with the opcode CONST_STRING and the string "Hello, ReVanced! Editing bytecode." - // This instruction will store the constant string reference in the register v1 - // For that a reference to the string is needed. It can be created by creating a ImmutableStringReference - val stringInstruction1 = BuilderInstruction21c( - Opcode.CONST_STRING, - 1, - ImmutableStringReference("Hello, ReVanced! Editing bytecode.") - ) - - // Replace the instruction at index startIndex with a new instruction - // We make sure to use this method to handle references to it via labels in any case - // If we are sure that the instruction is not referenced by any label, we can use the index operator overload - // of the instruction list: - // implementation.instructions[startIndex] = instruction - implementation.replaceInstruction(startIndex, stringInstruction1) - - // Now lets print our string twice! - - // Create the necessary instructions (we could also clone the existing ones) - val stringInstruction2 = BuilderInstruction21c( - Opcode.CONST_STRING, - 1, - ImmutableStringReference("Hello, ReVanced! Adding bytecode.") - ) - val invokeInstruction = BuilderInstruction35c( - Opcode.INVOKE_VIRTUAL, - 2, 0, 1, 0, 0, 0, - ImmutableMethodReference( - "Ljava.io.PrintStream;", - "println", - setOf("Ljava/lang/String;"), - "V" + // Replace the instruction at index startIndex with a new instruction. + // The instruction format can be found in the docs at + // https://source.android.com/devices/tech/dalvik/dalvik-bytecode + // + // In our case we want an instruction with the opcode CONST_STRING + // and the string "Hello, ReVanced! Adding bytecode.". + // The format is 21c, so we create a new BuilderInstruction21c + // This instruction will hold the string reference constant in the virtual register 1. + // For that a reference to the string is needed. It can be created with an ImmutableStringReference. + // At last, use the method replaceInstruction to replace it at the given index startIndex. + implementation.replaceInstruction( + startIndex, + BuilderInstruction21c( + Opcode.CONST_STRING, + 1, + ImmutableStringReference("Hello, ReVanced! Editing bytecode.") ) ) - // Insert our instructions after the second instruction by our pattern. - implementation.addInstruction(startIndex + 1, stringInstruction2) - implementation.addInstruction(startIndex + 3, invokeInstruction) + // Get the class in which the method matching our signature is defined in. + val mainClass = cache.findClass { + it.type == result.definingClassProxy.immutableClass.type + }!!.resolve() + + // Add a new method returning a string + mainClass.methods.add( + ImmutableMethod( + result.definingClassProxy.immutableClass.type, + "returnHello", + null, + "Ljava/lang/String;", + AccessFlags.PRIVATE or AccessFlags.STATIC, + null, + ImmutableMethodImplementation( + 1, + ImmutableList.of( + BuilderInstruction21c( + Opcode.CONST_STRING, + 0, + ImmutableStringReference("Hello, ReVanced! Adding bytecode.") + ), + BuilderInstruction11x(Opcode.RETURN_OBJECT, 0) + ), + null, + null + ) + ).toMutable() + ) + + // Now lets create a new call to our method and print the return value! + // You can also use the smali compiler to create instructions. + // For this sake of example I reuse the class field System.out inside the virtual register 0. + // Instead an additional instruction could be added at first to re-set this register. + // "sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;" + // + // Control flow instructions are not supported as of now. + val instructions = """ + invoke-static { }, LTestClass;->returnHello()Ljava/lang/String; + move-result-object v1 + invoke-virtual { v0, v1 }, Ljava/io/PrintStream;->println(Ljava/lang/String;)V + """.trimIndent().asInstructions() + implementation.addInstructions(startIndex + 2, instructions) + + // TODO: check TODO of the MutableEncodedValue class + //mainClass.fields.add( + // ImmutableField( + // mainClass.type, + // "dummyField", + // "Ljava/io/PrintStream", + // AccessFlags.PRIVATE or AccessFlags.STATIC, + // ImmutableFieldEncodedValue( + // ImmutableFieldReference( + // "Ljava/lang/System;", + // "out", + // "Ljava/io/PrintStream;" + // ) + // ), + // null + // ).toMutable() + //) // Finally, tell the patcher that this patch was a success. // You can also return PatchResultError with a message. @@ -118,11 +162,11 @@ internal class PatcherTest { @Test fun `test patcher with no changes`() { Patcher( - File(PatcherTest::class.java.getResource("/test1.apk")!!.toURI()), - File("/no-changes-test"), + File(PatcherTest::class.java.getResource("/test1.dex")!!.toURI()), + File("."), testSignatures ).save() // FIXME(Sculas): There seems to be a 1-byte difference, not sure what it is. // assertEquals(available, out.size()) } -} \ No newline at end of file +} diff --git a/src/test/resources/test1.dex b/src/test/resources/test1.dex new file mode 100644 index 0000000000000000000000000000000000000000..6bf4069364fd29ff0b1717b4e061267963d5e960 GIT binary patch literal 744 zcmYdEt>7{+Hf7izFYG6rE0|!=>G8`t%*%Us#m{FK+O7z{U}9h>U|?XV2s064fC3LD z1_nL`h&Vd~1H%di1_ovZ28JUH3=E763=A(A7#LU>7#Kc4`2vg#3_BPZ7z`L07|IwK z80r`q7kQm5$oD2*MAa({L0|Sg*zzA{=BLfde0UR?iaD&~) z$iT}0F&{34#0U8gByI_E08~B5iMk*+g2F%msV_;-p=VM?7krj|2YG`2D zz{JRTf(aakpl|~D4@Se%2q@@4;Rs77pl|@C6;ND))PvFsD9%89P?`b7El3=cZWt9H naRW*_i~!>NJL{*`JC&7BPZdVJ=po;KIPb;ELjkXf!YI zGcYiOq!yPr=Oh*v>m`H8+1cS7%o9B~a)JleZD z(|U^LWS36?e{^Csc6?y|qw&#Cr29hS%rCD|^pWrpH(LFencrFDNWe;4h4 zRyRS_XcqsL3w)1kW_EF}JlVU^WAnmCnN^EU^67-==2+c0Waj)gNJCWqyZmF9v)^>z zSll=&=ls_H=!Gb=o%3{#nEYmE_0Lpu`XuxG%Y%OwlQz~nml+wUR#+swSkoZ6rn)xR zr2F++Dc0I4FSmcux~TSCA$!VK4Wq87i_JHrdu3!;X|t}-%r2-~$*yuW<1OpbZ6CRc zJ-2tSVhc6pTqzzabsrSbGlJ(XR%2vfSjh;AXhtRx2Gn!{O9Y^F0!)KI~xG&Sm$T} diff --git a/src/test/resources/test2.dex b/src/test/resources/test2.dex new file mode 100644 index 0000000000000000000000000000000000000000..f553846d2278c7872386aa23d8005ceb8d06962d GIT binary patch literal 916 zcmYdEt>7{+Hf3lm3wmoKF3omm?bctM2`eloODJ5q*6X)%3Nr&k0Rsa=MVN^Y0~CB= zVqg$tfQWN2FfeRkU|?WoU|_hyz`(%Bz`*c>fq{Vyq?VC^fr){E!GMv0L4}Edp@5Nr z;TahHs1v3@l6x3_?r{44O;~47N-R41r7x45>^E3}s9V4DCz|3|&kN41G)t z42zi<7*;VcFl=CAVA#q8F%x7T3j+fKE7UG_1_lN$1_lNmC=Irsfq{V=Du06!!G6F9 z(#OQW4{{?MGcoW%-47B1`Gc1MWEUfY0MvgVaTo^K4`PG-52CMvJc^_rCeI0yWME); z43}pFhtWPJ1_qG(3j{zu;A4P@GB6y5iWh=I6e13C%PFWhOg+dr5I)buzyNYX5(5K+ z6eA-;o&pPl00T%L#C(umb_O<^%)HDJI|eq7)SR4rMFt+9kksN5=bXgiVnb_Wo{=?! zs83d6S)zVszJ5SaW?o5fNl|KIt~G-YL^vlgFJ0e1DJwO(1XV1!q$o2l9ZjsVxFj{# znt>l-n;yt;BmpCc0Am;fQ`z4>ix|PKFc+&(aA9Cza7A%N0GbyB z7#J8rQj1HRa}tY-4fT>ilyBcbKPE$gw*QIKW7n+iPS`WEfQ3(}$Glfu$e)E@KyTiz z8ws8-)pmEkf9iSYAp3`fJ(t^3W~MEhT)*$+Kl&o4i>+QCgT4F}VYg(dL>ONfd-|X%Qe@mlF>)b!Dx={A_h1gM> z`=Z)k7s<}i3yJjY(K-Eb?qXpR!J{u!UMMAPlM}wR^=|gIz}2rx>ije6G?I(H2z?ZP)h1a%6^9Sp7C!fv`J_E4md+lT z$^D3dfx(CoIhOE-j}at%awnYkI_w~DY=7>gJmJMF*M8lf!0+_>U_-^~3eAr81x{Y? zXYaZ)#aHR1+lP%`7CZoj&=F6*u!F64XZ}6={Vd<_pI`qmKH&MyH!EWE!-aPb?P})R zB_l0k+xDz-&aq!j2PJrx^E|WIJ((v?VELzAK1wtFU6yo2d=FaLbC7eh(fpIAcQoB| z5vy9`5;$j`&XL6h2QNOlQL?c$_f>6ZcrpImUJx|2OLomVFh|m2%|5Uv}dU?XQ*ved2A3*EN0kGsEJjqRy`gS*976 z&m?Tnv|eyRBkFj5!ucr|PL!BsM|g(SEpgu}x*`6;3yJjx(~FktNvfE3?)a*``aUR7 zZRSaZh%+)U^ne4Ekx7IBHRr&x1t{l0a|o0|$wvX+s9KTJGAQ3b^)N6n*y6}V$R>c| w6gjp*X&zz1VJtZST{p6~P<20MM9M$V^bz3A$_6r@g@J|PCj$e61v7{T008H3I{*Lx